ESP32 게임 콘솔 펌웨어 올려보기
목차
얼마전이라고 생각했던 “ESP32 게임 콘솔 보드” 구입이 지나고 보니 꽤 오래 된거 같네요 한창 레트로 게임에 관심이 있었던지라 게임기 FW를 직접 다뤄볼까? 라는 생각이 들어서 ESP32 기반의 게임 콘솔 보드를 알리 익스프레스에서 구입을 했었습니다.
구입을 하고 나서 직후에 올려보려고 했으나 ESP32 개발 환경만 꾸미고 말았네요. 그렇게 제 방 한 구석에 쳐박혀 있다가 계속 놔두기 아까워서 다시 시도해 보기로 결심했습니다.
또한 ESP32 로 어느정도 게임을 구동할 수 있을지 궁금하기도 하더군요. 그래서 이번에는 제대로 도전해 보기로 했습니다.
Esplay 의 오픈소스를 이용하여 펌웨어 파일 올려보기
제가 알리에서 구입했던 ESP32 기반의 게임 콘솔 보드에 펌웨어를 올려보기 위해서 정보를 찾기 위해 다시 알리 익스프레스를 찾았는데… 해당 보드를 판매하는 샵들이 눈에 띄게 줄어들었습니다. 알리에서도 그리 수요가 많지 않았을까요?

그래도 현재 팔기는 팔고 있습니다. 그중에 한 판매샵을 확인해 보니 위와 같이 LCD에 ESPLAY 라고 표기가 되어 있네요. 그래서 일단 ESPLAY 를 기준으로 올려보기로 하였습니다(하드웨어가 ESPLAY와 유사합니다). 일단 필요한 소스는 크게 2가지로 되어 있습니다. ESPLAY base firmware 와 에뮬레이터 소스 파일 입니다.
- ESP32 WROVER, Dual core processor with Integrated 4MB Flash + 4MB PSRAM
- Integrated WIFI and Bluetooth 4 BLE
- 2,4″ ILI9341 TFT Panel
- More button (expanded via PCF8574 I2C GPIO)
- Micro SD slot connected to SDMMC Host in 1 Line Mode for save GPIO pin
- Integrated I2S DAC via UDA1334A
- Integrated USB to Serial for programming and debugging using CH340C
- Built-in TP4056 Li-Po charger
- I2C port for expandable function
- 3,5mm Headphone jack
- Smaller Size, PCB only 100 x 50 mm.
ESPLAY의 하드웨어 정보인데 이와 유사 보드라고 보심 되겠습니다.
참고로 ESPLAY의 펌웨어 소스는 오드로이드-고를 기반으로 참고하여 개선했다고 합니다.
ODROID-GO 는 국내 오픈소스 하드웨어 제조사인 하드커널에서 창립 10주년으로 출시한 ESP32 기반의 오픈소스 게임기입니다. 그런데 현재는 단종이 되어서 실제 판매는 하지 않는군요.
ESPLAY 펌웨어 소스 빌드 환경 셋팅
먼저 ESPLAY 펌웨어 깃허브 소스를 내 보드에 올려보려고 합니다.
그런데 이 소스는 “Use esp-idf official release version 4.4” 와 같이 4.4 버전을 사용했다고 합니다. 그래서 일단은 IDF 를 v4.4 를 써야 할꺼 같습니다. 현재 제 PC에 설치되어 있는 버전은 v5.2.1 이네요(물론 이 버전도 좀 오래 되었습니다)
C:\Espressif\frameworks\esp-idf-v5.2.1>
현재 제 PC의 esp-idf 는 위와 같은 경로에 설치가 되어 있습니다. 일단 v4.4 버전을 설치해 봐야겠네요. v4.4 는 예전 버전이라서 설치 매니저에서 설치가 불가능한거 같습니다.
cd C:\Espressif\frameworks git clone -b v4.4 --recursive https://github.com/espressif/esp-idf.git esp-idf-v4.4
그래서 위 명령어로 v4.4 를 별도로 clone 하겠습니다. 기존 v5.2.1 과 별도로 설치를 해야 하니깐요.
만약 파이썬이 설치 되어 있지 않다면 파이썬을 설치해 주세요. 파이썬이 설치되어 있어야 v4.4 설치가 가능합니다. 또한 파이썬 버전은 3.10.11 버전을 설치해야 할거 같네요. 상위 버전은 설치에 오류가 나는거 같습니다.
https://www.python.org/ftp/python/3.10.11/python-3.10.11-amd64.exe
3.10.11 버전이 없다면 위 링크에서 다운로드 받으시면 됩니다.
파이썬이 준비되었다면 다음과 같이 v4.4 를 설치할 경로를 확인해 봅니다.
$env:IDF_TOOLS_PATH = "C:\Espressif\idf44_tools" Test-Path C:\Espressif\idf44_tools\idf-env.json
기존 설치된 버전과 분리하기 위해서 다음과 같이 파워쉘에서 실행시켜 준다음에 False 인지 확인합니다.
$env:IDF_PATH = "C:\Espressif\frameworks\esp-idf-v4.4" $env:IDF_TOOLS_PATH = "C:\Espressif\idf44_tools" C:\Python310\python.exe C:\Espressif\frameworks\esp-idf-v4.4\tools\idf_tools.py install --targets esp32
그런 다음 위의 명령어를 통해 설치를 진행합니다. v4.4 에 필요한 도구들을 설치해야 합니다.

위와 같이 나오면 일단 v4.4 개발 도구들은 설치가 완료된 겁니다. 하지만 v4.4 를 빌드할 때 필요한 파이썬 가상 환경의 설치가 되지 않아서 설치를 해줘야 합니다.

개발 도구들은 다 설치가 되어 있지만 파이썬 가상 환경이 빠져 있으므로 추가로 다음과 같이 설치 해 줍니다.
$env:IDF_TOOLS_PATH = "C:\Espressif\idf44_tools" $env:IDF_PATH = "C:\Espressif\frameworks\esp-idf-v4.4" C:\Python310\python.exe C:\Espressif\frameworks\esp-idf-v4.4\tools\idf_tools.py install-python-env

그런 다음에 esp-idf-v4.4 폴더에서 export.ps1 을 파워쉘에서 실행시켜주면 빌드 환경 설정이 마무리 된겁니다. 이제 Esplay 소스를 빌드 할 수 있습니다.
VS Code 에 v4.4 버전 연결하기
먼저 ESPLAY 코드를 git 으로 clone 합니다.
https://github.com/pebri86/esplay-base-firmware.git
그런 다음에 해당 폴더를 VS Code 로 열어 줍니다.
#.vscode/setting.json 파일
{
"idf.espIdfPathWin": "C:/Espressif/frameworks/esp-idf-v4.4",
"idf.pythonBinPathWin": "C:/Espressif/idf44_tools/python_env/idf4.4_py3.10_env/Scripts/python.exe",
"idf.toolsPathWin": "C:/Espressif/idf44_tools"
}
이후에 VS Code 창을 Reload 하고 VS Code 의 터미널에서 아래와 같이 실행하면 환경 설정이 완료됩니다.
$env:IDF_TOOLS_PATH = "C:\Espressif\idf44_tools" . "C:\Espressif\frameworks\esp-idf-v4.4\export.ps1"
위 명렁어는 터미널 창이 닫히면 다시 실행해 줘야 되는군요.
idf.py reconfigure
위 명령어 실행 후 다음과 같이 나오면 정상적으로 빌드가 가능한 상태입니다.
/components/nvs_flash C:/Espressif/frameworks/esp-idf-v4.4/components/openssl C:/Espressif/frameworks/esp-idf-v4.4/components/openthread C:/Espressif/frameworks/esp-idf-v4.4/components/partition_table C:/Espressif/frameworks/esp-idf-v4.4/components/perfmon C:/Espressif/frameworks/esp-idf-v4.4/components/protobuf-c C:/Espressif/frameworks/esp-idf-v4.4/components/protocomm C:/Espressif/frameworks/esp-idf-v4.4/components/pthread C:/Espressif/frameworks/esp-idf-v4.4/components/sdmmc C:/Espressif/frameworks/esp-idf-v4.4/components/soc C:/Espressif/frameworks/esp-idf-v4.4/components/spi_flash C:/Espressif/frameworks/esp-idf-v4.4/components/spiffs C:/Espressif/frameworks/esp-idf-v4.4/components/tcp_transport C:/Espressif/frameworks/esp-idf-v4.4/components/tcpip_adapter C:/Espressif/frameworks/esp-idf-v4.4/components/tinyusb D:/ESP32/esplay-base-firmware/components/ugui C:/Espressif/frameworks/esp-idf-v4.4/components/ulp C:/Espressif/frameworks/esp-idf-v4.4/components/unity C:/Espressif/frameworks/esp-idf-v4.4/components/usb C:/Espressif/frameworks/esp-idf-v4.4/components/vfs C:/Espressif/frameworks/esp-idf-v4.4/components/wear_levelling C:/Espressif/frameworks/esp-idf-v4.4/components/wifi_provisioning C:/Espressif/frameworks/esp-idf-v4.4/components/wpa_supplicant C:/Espressif/frameworks/esp-idf-v4.4/components/xtensa
-- Configuring done
-- Generating done
-- Build files have been written to: D:/ESP32/esplay-base-firmware/build
그런 다음에 다음과 같이 명령어를 실행하면 빌드가 진행됩니다.
idf.py build
빌드 오류시 패치하기
일단 ESPLAY 의 깃허브 소스 그대로 빌드하면 에러가 발생합니다. 따라서 간단한 패치를 해줘야 합니다.
/* 요건 v4.4 의 안에 있는 esp_partition.h 파일입니다 @file components/spi_flash/include/esp_partition.h */ /* 하기 함수를 추가해 줍니다 */ void esp_partition_reload_table();
/* 요건 v4.4 의 안에 있는 partition.c 파일입니다
@file components/spi_flash/partition.c */
/* 하기 함수를 추가해 주세요 */
void esp_partition_reload_table()
{
if (!SLIST_EMPTY(&s_partition_list))
{
_lock_acquire(&s_partition_list_lock);
// Remove all entries
while(!SLIST_EMPTY(&s_partition_list))
{
partition_list_item_t* item = SLIST_FIRST(&s_partition_list);
SLIST_REMOVE_HEAD(&s_partition_list, next);
free(item);
}
_lock_release(&s_partition_list_lock);
}
}
파티션 관련 함수인데 v4.4 SDK 안에서는 없어서 에러가 발생합니다. 추가해 주고 빌드하면 에러 없이 빌드가 될겁니다.
펌웨어 파일 ESP32 게임 보드에 다운로드 하기
자 그럼 빌드된 펌웨어 파일들을 ESP32 게임 보드에 다운로드를 해봐야 겠습니다.

보드에는 위와 같이 USB-C 타입의 커넥터가 있는데 이걸 PC와 연결해 줍니다.

그러면 위와 같이 PC의 장치관리자에서 USB 시리얼 장치가 나타납니다. 이걸 사용해서 다운로드를 할 겁니다. 해당 ESP32 가 어떤 모델인지 확인하기 위해서 esptool.py 를 이용하여 확인해 봐야겠습니다.
python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py --port COM5 chip_id
흠 그런데 다음과 같은 에러가 발생하는 군요.
PS D:\ESP32\esplay-base-firmware> python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py --port COM5 chip_id
esptool.py v3.2-dev
Serial port COM5
Connecting......................................
A fatal error occurred: Failed to connect to Espressif device: Wrong boot mode detected (0xb)! The chip needs to be in download mode.
For troubleshooting steps visit: https://github.com/espressif/esptool#troubleshooting
찾아보니 boot mode 가 설정이 안되어 있어서 그런거 같습니다. 게임보드를 확인해 보니 BOOT 핀이 별도로 존재하네요. 그런데 핀 헤더가 납땜이 되어 있지 않아서 오랜만에 직접 인두기로 핀 헤더를 납땜을 하였습니다.

요렇게 BOOT 핀을 연결해야 하는거 같습니다.

그런 다음에 위와 같이 점퍼로 연결해주면 됩니다.
다시 esptool 로 확인을 해보니
PS D:\ESP32\esplay-base-firmware> python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py --port COM5 chip_id
esptool.py v3.2-dev
Serial port COM5
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting......
Detecting chip type... ESP32
Chip is ESP32-D0WD (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 08:b6:1f:ae:9e:8c
Uploading stub...
Running stub...
Stub running...
Warning: ESP32 has no Chip ID. Reading MAC instead.
MAC: 08:b6:1f:ae:9e:8c
Hard resetting via RTS pin...
오호라… 이제 chip_id 값을 읽어오는군요. “ESP32-D0WD” 라고 나옵니다. 그리고 Mac 주소 까지 읽어오는 군요. 이제 펌웨어 파일을 다운로드 할 준비가 된거 같습니다.
PS D:\ESP32\esplay-base-firmware> python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py --port COM5 flash_id
esptool.py v3.2-dev
Serial port COM5
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting.....
Detecting chip type... ESP32
Chip is ESP32-D0WD (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 08:b6:1f:ae:9e:8c
Uploading stub...
Running stub...
Stub running...
Manufacturer: 5e
Device: 4016
Detected flash size: 4MB
Hard resetting via RTS pin...
해당 보드의 Flash 용량이 얼마인지 확인해 보니 4MB 가 확인이 되네요. Flash 용량은 다음과 같은 명령어로 확인할 수 있습니다.
python C:\Espressif\frameworks\esp-idf-v4.4\components\esptool_py\esptool\esptool.py --port COM5 flash_id
그런 다음에 target 를 지정해줍니다. 제 보드는 esp32 이므로 아래 명령어로 지정한 다음에 혹시 몰라서 build 를 다시 했습니다.
idf.py set-target esp32 idf.py build
빌드가 완료되고 이제 만들어진 펌웨어 파일을 보드에 다운로드를 해보겠습니다. 아래 명령어로 다운로드를 하면 됩니다.
idf.py -p COM5 flash
Writing at 0x0001e307... (18 %)
Writing at 0x000286e3... (27 %)
Writing at 0x0002df2e... (36 %)
Writing at 0x00033ba8... (45 %)
Writing at 0x00038eaf... (54 %)
Writing at 0x0003e8b4... (63 %)
Writing at 0x00044638... (72 %)
Writing at 0x00049ea7... (81 %)
Writing at 0x000526eb... (90 %)
Writing at 0x0005a916... (100 %)
Wrote 323792 bytes (175618 compressed) at 0x00010000 in 4.4 seconds (effective 587.3 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 329.6 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Done
위와 같이 파워쉘 창에 나타나면 다운로드가 완료된겁니다. 그랬더니

오호라.. 일단 LCD 화면에 출력되고 부팅까지 정상적으로 되는거 확인하였습니다.
idf.py -p COM5 monitor
위 명령어를 입력하면 시리얼 콘솔 로그를 같이 모니터링 할 수 있습니다.
I (326) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (332) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (338) heap_init: At 4008F038 len 00010FC8 (67 KiB): IRAM
I (346) spi_flash: detected chip: generic
I (349) spi_flash: flash io: dio
W (353) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (367) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
esplay-base-firmware (Ver: 20261029-5f6f5df). HEAP=0x00021f9c
I (418) gpio: GPIO[34]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:3
I (418) gpio: GPIO[36]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:3
input_gamepad_init done.
Backlight initialization done.
I (438) gpio: GPIO[12]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
E (4668) sdmmc_common: sdmmc_init_ocr: send_op_cond (1) returned 0x107
E (4668) vfs_fat_sdmmc: sdmmc_card_init failed (0x107).
sdcard_open: esp_vfs_fat_sdmmc_mount failed (263)
ui_choose_file: HEAP=0x000194f8
opendir failed.
ui_choose_file: fileCount=0
ui_draw_page: HEAP=0x000184f8
시리얼 로그를 보니 SD카드 인식 실패가 나오네요. SD 카드를 넣어줘야 할거 같습니다.
런쳐 소스 빌드하여 올리기
런쳐 소스 빌드 해보기
여기까지 진행이 되었다면 이제 실제 게임을 구동시킬 때 사용하는 “런쳐 소스”를 빌드하여 게임 보드에 올려야 합니다.
위 펌웨어 빌드 소스와 같은 개발자가 올려놓은 소스인데 여기에 에뮬레이터가 같이 있는걸로 판단하고 이걸 빌드하여 올려보기로 했습니다.
위 펌웨어 소스는 ESP-IDF 버전이 v4.4 였는데 요건 readme 설명에서는 v3.3 이라고 하였으나 실제 커밋 내역을 보면 v5.5.x 까지 지원하도록 업데이트 한거 같아 보였습니다. 그래서 제 PC에 v5.5.6 SDK를 별도로 설치하였습니다. (설치과정은 생략합니다)
-- Configuring incomplete, errors occurred!
HINT: The component 'appfs' could not be found. This could be because: component name was misspelled, the component was not added to the build, the component has been moved to the IDF component manager, the component has been removed and refactored into some other component or the component may not be supported by the selected target.
Please look out for component in 'https://components.espressif.com' and add using 'idf.py add-dependency' command.
Refer to the migration guide for more details about moved components.
Refer to the build-system guide for more details about how components are found and included in the build.
cmake failed with exit code 1, output of the command is in the D:\ESP32\esplay-retro-emulation\launcher\build\log\idf_py_stderr_output_8980 and D:\ESP32\esplay-retro-emulation\launcher\build\log\idf_py_stdout_output_8980
(venv) PS D:\ESP32\esplay-retro-emulation\launcher> cd ..
(venv) PS D:\ESP32\esplay-retro-emulation> git submodule update --init --recursive
Submodule 'launcher/components/appfs' (https://github.com/badgeteam/esp32-component-appfs.git) registered for path 'launcher/components/appfs'
Cloning into 'D:/ESP32/esplay-retro-emulation/launcher/components/appfs'...
Submodule path 'launcher/components/appfs': checked out '4d09f2377406f8cdaca7e9b017f8c9dd99462cee'
(venv) PS D:\ESP32\esplay-retro-emulation> git submodule status
4d09f2377406f8cdaca7e9b017f8c9dd99462cee launcher/components/appfs (v1.0.2)
git clone 으로 해당 소스를 내려 받은 다음에 idf.py 로 빌드를 하다가 요렇게 에러가 발생하더군요. appfs 라는게 없어서 확인해 보니 서브 모듈을 내려 받지 않아서 생긴 문제입니다.
git submodule update --init --recursive
해당 소스의 최 상단에서 서브모듈을 내려받고 나서 빌드를 해보니 정상적으로 빌드가 되네요.
esptool.py v4.12.0
Creating esp32 image...
Merged 2 ELF sections
Successfully created esp32 image.
Generated D:/ESP32/esplay-retro-emulation/launcher/build/launcher.bin
[1829/1830] C:\WINDOWS\system32\cmd.exe /C "cd /D D:\ESP32\esplay-retro-emulation\launcher\build\esp-i...build/partition_table/partition-table.bin D:/ESP32/esplay-retro-emulation/launcher/build/launcher.bin"
launcher.bin binary size 0x13c8d0 bytes. Smallest app partition is 0x1f0000 bytes. 0xb3730 bytes (36%) free.
Project build complete. To flash, run:
idf.py flash
or
idf.py -p PORT flash
or
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size detect --flash_freq 40m 0x1000 build\bootloader\bootloader.bin 0x9000 build\partition_table\partition-table.bin 0x13000 build\ota_data_initial.bin 0x20000 build\launcher.bin
or from the "D:\ESP32\esplay-retro-emulation\launcher\build" directory
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"
(venv) PS D:\ESP32\esplay-retro-emulation\launcher>
요렇게 빌드가 완료되었습니다.
자 그럼 이 생성된 파일을 보드로 다운로드 하였습니다.
Writing at 0x00128f4b... (83 %)
Writing at 0x00130904... (85 %)
Writing at 0x00138a5a... (87 %)
Writing at 0x0013ef07... (89 %)
Writing at 0x00145231... (91 %)
Writing at 0x0014ae61... (93 %)
Writing at 0x001511d4... (95 %)
Writing at 0x00156a16... (97 %)
Writing at 0x0015c520... (100 %)
Wrote 1296592 bytes (787109 compressed) at 0x00020000 in 19.0 seconds (effective 545.9 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 131...
Writing at 0x00009000... (100 %)
Wrote 3072 bytes (131 compressed) at 0x00009000 in 0.1 seconds (effective 333.5 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 31...
Writing at 0x00013000... (100 %)
Wrote 8192 bytes (31 compressed) at 0x00013000 in 0.1 seconds (effective 447.7 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Done
위와 같이 다운로드가 성공적으로 끝나서 부팅을 시켜보니… 어 이런 LCD 화면에 아무런 화면이 뜨질 않습니다. 시리얼 모니터로 확인해 보니 파티션 오류가 나는군요. 알아보니 위 소스의 Flash 용량이 16 MB로 설정이 되어 있었습니다.
그래서 크게 2가지를 바꿔줘야 하는데 일단 아래와 같이 16 MB로 설정되어 있는 것을 4MB로 바꿔줘야 함. (partitions.csv)
# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, , 0x9000, otadata, data, ota, , 0x2000, phy_init, data, phy, , 0x1000, launcher, app, factory, , 0x1F0000, appfs, 0x43, 0x03, , 0x1D0000, coredump, data, coredump, , 0x10000,
위 파일의 위치는 luancher 라는 폴더 안에 있습니다. 여기서 appfs 가 0xDD0000 으로 되어 있는 걸 0x1D0000 으로 바꿨습니다.
그런 다음에 ESP32 menuconfig 를 통해 플래시 용량을 바꿔줘야 합니다.
idf.py menuconfig

위 그림에서와 같이 Flash size 를 16 MB -> 4 MB로 변경해야 합니다. config 를 저장하고 나서 다음에 다시 소스를 빌드하고 보드에 다운로드를 해보니

짠 이렇게 부팅이 되는게 확인이 됩니다. ESPLAY3 이라고 표시가 되네요. 성공인줄 알았지만
그런데… 아이콘 안에는 아무것도 들어 있지 않습니다. 일단 키는 잘 먹는데 실행할 수 있는 에뮬레이터 파일이 없더군요.
idf.py -p COM5 monitor
시리얼 모니터 명령으로 확인해보니 시리얼 콘솔 상으로도 정상적으로 부팅되는거 확인하였습니다. 다만 SD 카드가 없어서 인식 오류가 나므로 아마 SD 카드 때문인걸로 판단하였습니다. 알아보니 일단 위 소스는 런처 소스인걸로 보이네요.
그래서 별도의 fw 파일(에뮬레이터 파일)을 SD 카드에 넣어줘야 할꺼 같아서 찾아보다가 위 개발자의 깃허브 레파지토리에 다음과 같이 펌웨어 모음 파일을 발견했습니다.
위 깃허브 소스에 가면 fw 파일로 된 에뮬레이터 파일들이 있습니다. 요걸 내 SD 카드에 복사하여 넣으면 될줄 알았으나….
정상적으로 에뮬레이터 들을 로드 하지 못하더군요.
static void lv_populate_list_items(lv_obj_t *list, int type) {
switch (type) {
case LIST_APP:
lv_add_file_list(list, "*.app");
break;
case LIST_GAMES:
lv_add_file_list(list, "*.emu,*.game");
break;
case LIST_SETTINGS:
lv_add_setting_list(list);
break;
default:
break;
}
}
// luancher_main.c 소스에는 .app/.emu/.game 파일을 read 하게 되어 있다.
런처의 소스에는 위 소스에서 보이듯이 .app/.emu/.game 파일들을 로드하는거 같습니다. 관련하여 위 레포지토리에 git 서브 모듈로 에뮬레이터 소스들이 연결되어 있는줄 알았으나… 없는걸 보고 다시 좀 찾아봤으나 못찾아서 포기.
일단 개발자가 ESPLAY 3 버전까지는 작업을 했으나 에뮬레이터 등의 작업은 아직 안한거 같다는 판단이 듭니다. 위 개발자가 v3.3 SDK 용으로 배포한 소스가 있었으나 v3.3 은 너무 오래된 환경이라 다른 대안을 찾아보기로 했습니다.
Retro-GO 소스 빌드하여 올리기
결국 돌고 돌아서 여기까지 다시 왔습니다. ESP-IDF 버전 중에 v5.5.6 을 사용할 수 있고 에뮬레이터까지 포함된 오픈소스가 있는지 찾아보다가 플랫폼화 된 오픈소스를 발견했네요.
바로 요 소스입니다. 이 소스는 ODRIOD-GO 기반의 게임 소스인데 시중에 출시된 다양한 ESP32 기반의 오픈 하드웨어 게임기들을 지원하는 통합 소스이군요.
ESP-IDF v5.5.X 이상에서 빌드가 가능하다고 하니 현재 설치한 버전에서 빌드를 해도 크게 문제가 없을거 같기도 합니다. 그래서 이 소스를 가지고 제가 가지고 있는 보드에 올려 보기로 했습니다.
retro-go 빌드
python .\rg_tool.py
usage: rg_tool.py [-h]
[--target {sdl2,rachel-esp32,redroid-go,odroid-go,vmu,fri3d-2024,retro-ruler-V1,brutzelboy,retro-esp32,mrgc-gbm,t-deck-plus,mrgc-g32,esp32-s3-devkit,byteboi-rev1,nullnano,crokpocket,esplay-micro}]
[--no-networking] [--port PORT] [--baud BAUD] [--fatsize FATSIZE]
{build-fw,build-img,release,build,clean,flash,monitor,run,profile,install} [{all,launcher,retro-core,prboom-go,gwenesis,fmsx} ...]
rg_tool.py: error: the following arguments are required: command
retro-go 는 소스 안에 포함된 rg_tool.py 를 이용하여 빌드를 하는거 같더군요. 소스들이 여러 개로 나눠져 있는데 이걸 한꺼번에 빌드해주는 파일입니다. 처음에 빌드를 하기 위해서 rg_tool.py help 옵션을 실행해 봤습니다.
저도 처음엔 헷갈렸는데 retro-go 소스가 여러 하드웨어를 지원하고 있어서 이중에 지원하는 하드웨어를 선택하는게 가능합니다. 위 옵션에 보면 –target 에 여러 하드웨어 들이 있는데 마친 esplay-micro 라는게 보이는군요. retro-go 에 대한 내용을 찾아보니 ESPLAY 하드웨어도 retro-go 에 포함을 시켜 배포를 하고 있다고 합니다.
자 그럼 타겟을 esplay-micro 로 선택하고 빌드하려면 아래 명령어로 빌드를 해주면 됩니다.
python rg_tool.py --target esplay-micro build
이렇게 하면 retro-go 의 모든 소스들이 esplay-micro 에 맞게 빌드가 됩니다.
Successfully created esp32 image.
Generated D:/ESP32/retro-go/fmsx/build/fmsx.bin
[920/920] C:\WINDOWS\system32\cmd.exe /C "cd /D D:\ESP32\retro-go\fmsx\build\esp-idf\e.../fmsx/build/partition_table/partition-table.bin D:/ESP32/retro-go/fmsx/build/fmsx.bin"
fmsx.bin binary size 0xa02a0 bytes. Smallest app partition is 0x300000 bytes. 0x25fd60 bytes (79%) free.
App build complete. To flash, run:
idf.py app-flash
or
idf.py -p PORT app-flash
or
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash 0x10000 build\fmsx.bin
or from the "D:\ESP32\retro-go\fmsx\build" directory
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"
Done.
All done!
일단 이렇게 빌드가 완료가 됐는데 내 보드에 어떻게 다운로드를 하는지 잘 모르겠더군요. 여기서도 살짝 헤맸는데 각 하위 폴더 소스별로 따로 .bin 파일을 하나로 묶어줘야 되더군요. 이따 사용하는 옵션이 build-img 입니다.
python rg_tool.py --target=esplay-micro build-img
이렇게 하면 최 상위 폴더에 .img 파일을 생성하게 됩니다. 여러 하위 소스들을 하나로 묶어주는 역할을 하는거 같네요.
Bootloader build complete. To flash, run:
idf.py bootloader-flash
or
idf.py -p PORT bootloader-flash
or
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash --flash_mode dio --flash_size 16MB --flash_freq 80m 0x1000 build\bootloader\bootloader.bin
or from the "D:\ESP32\retro-go-codedosa\launcher\build" directory
python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args"
Running command: tools\mkfw.py --type esp32 --name Retro-Go --icon assets/icon.raw --version 1.46-8-g4ced1 --target esplay-micro --bootloader D:\ESP32\retro-go-codedosa\launcher\build\bootloader\bootloader.bin retro-go_1.46-8-g4ced1_esplay-micro.img 0 16 1048576 launcher launcher\build\launcher.bin 0 17 1048576 retro-core retro-core\build\retro-core.bin 0 18 786432 prboom-go prboom-go\build\prboom-go.bin 0 19 1048576 gwenesis gwenesis\build\gwenesis.bin 0 20 589824 fmsx fmsx\build\fmsx.bin
Partitions:
[0]: type=0, subtype=16, size=1114112 (97% used), label=launcher
> WARNING: File larger than partition (+32256 bytes), increased size to 1114112
[1]: type=0, subtype=17, size=1048576 (97% used), label=retro-core
[2]: type=0, subtype=18, size=851968 (97% used), label=prboom-go
> WARNING: File larger than partition (+44064 bytes), increased size to 851968
[3]: type=0, subtype=19, size=1048576 (94% used), label=gwenesis
[4]: type=0, subtype=20, size=720896 (91% used), label=fmsx
> WARNING: File larger than partition (+66208 bytes), increased size to 720896
Flash size: 4.625 MB
File 'retro-go_1.46-8-g4ced1_esplay-micro.img' successfully created.
All done!
.img 파일을 성공적으로 만들었습니다. 그런데…. 파일 크기가 살짝 이상하군요. 4.625 MB 인데 이게 현재 가지고 있는 보드의 Flash 보다 사이즈가 큽니다.
일단 다운로드를 해보기로 했습니다. 다운로드는 esptool.py 를 사용하여 하면 됩니다.
esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 "파일명"
esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 retro-go_1.46-8-g4ced1_esplay-micro.img
esptool.py v4.12.0
Serial port COM5
Connecting....
Chip is ESP32-D0WD (revision v1.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 08:b6:1f:ae:9e:8c
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
A fatal error occurred: File retro-go_1.46-8-g4ced1_esplay-micro.img (length 4849920) at offset 0 will not fit in 4194304 bytes of flash. Change the --flash_size argument, or flashing address.
아 역시나 에러가 발생합니다. 파일크기가 연결된 보드의 Flash 사이즈보다 커서 에러가 발생한 겁니다. 에러의 원인을 찾아봐야 겠습니다.
ESP32 보드의 Flash 용량 조절 패치
현재 가지고 있는 보드는 Flash가 4 MB 입니다. 파일이 4 MB가 훌쩍 넘기 때문에 일단 파일 사이즈를 줄여야 하는군요.
알아보니 빌드된 .img 파일 크기를 좀 줄여야 할거 같습니다.
| 디렉터리 | 역할 |
|---|---|
| launcher | 게임 목록 및 실행을 담당하는 런처 |
| retro-core | 여러 레트로 게임 에뮬레이터 통합 |
| prboom-go | DOOM 에뮬레이터 |
| gwenesis | Sega Mega Drive / Genesis 에뮬레이터 |
| fmsx | MSX 에뮬레이터 |
| components/retro-go | 공통 라이브러리 및 하드웨어 추상화 |
| tools | 펌웨어 이미지 생성 등의 도구 |
retro-go 의 하위 디렉토리(폴더)의 구조입니다. 여기서 필수로 들어가야 하는 컴포넌트는 launcher 와 retro-core 입니다. 이 두개는 꼭 들어가야 한다고 하네요.
참고로 어떤 에뮬레이터로 구성되어 있을까요?
| 애플리케이션 | 지원 시스템 |
|---|---|
| retro-core | NES, PCE, Game & Watch, Atari Lynx, SMS, Game Gear, ColecoVision 등 |
| prboom-go | DOOM |
| gwenesis | Sega Mega Drive / Genesis |
| fmsx | MSX / MSX2 |
일단 launcher 를 반드시 들어가야 하는 어플이고 위와 같은 구성으로 되어 있습니다. retro-core 는 꼭 필요할거 같고 나머지 에뮬들은 용량에 따라서 넣고 빼고 하면 되겠네요.
python rg_tool.py --target=esplay-micro build-img launcher retro-core python rg_tool.py --target=esplay-micro clean launcher retro-core esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 retro-go_1.46-8-g4ced1-dirty_esplay-micro.img
일단 launcher 와 retro-core 만 선택하여 img 파일을 만들어 보도록 하겠습니다.
=== Step: Packing ===
Building firmware image with: launcher retro-core
Building bootloader...
Running command: tools\mkfw.py --type esp32 --name Retro-Go --icon assets/icon.raw --version 1.46-8-g4ced1 --target esplay-micro --bootloader D:\ESP32\retro-go-codedosa\launcher\build\bootloader\bootloader.bin retro-go_1.46-8-g4ced1_esplay-micro.img 0 16 1048576 launcher launcher\build\launcher.bin 0 17 1048576 retro-core retro-core\build\retro-core.bin
Partitions:
[0]: type=0, subtype=16, size=1114112 (97% used), label=launcher
> WARNING: File larger than partition (+32256 bytes), increased size to 1114112
[1]: type=0, subtype=17, size=1048576 (97% used), label=retro-core
Flash size: 2.125 MB
File 'retro-go_1.46-8-g4ced1_esplay-micro.img' successfully created.
All done!
그랬더니 2.1 MB 정도 되는군요. 이 정도면 보드의 Flash 에 충분히 들어갈 거 같습니다. 그럼 esptool.py 로 다운로드를 해보지요.
esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 retro-go_1.46-8-g4ced1_esplay-micro.img
esptool.py v4.12.0
Serial port COM5
Connecting..................................
Chip is ESP32-D0WD (revision v1.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 08:b6:1f:ae:9e:8c
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00000000 to 0x00220fff...
Compressed 2228480 bytes to 1357205...
Wrote 2228480 bytes (1357205 compressed) at 0x00000000 in 119.9 seconds (effective 148.7 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
드디어 정상적으로 다운로드가 됐습니다. 잘 부팅될지 기대가 되는군요. 그런데…. LCD가 켜지지 않습니다. 어떤 문제인지 몰라서 시리얼로 확인해 봐야 겠습니다.
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x1b (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:5408
load:0x40078000,len:16508
load:0x40080400,len:4232
--- 0x40080400: _invalid_pc_placeholder at D:/esp/v5.5.6/esp-idf/components/xtensa/xtensa_vectors.S:2259
entry 0x40080644
--- 0x40080644: call_start_cpu0 at D:/esp/v5.5.6/esp-idf/components/bootloader/subproject/main/bootloader_start.c:27
I (27) boot: ESP-IDF v5.5.6 2nd stage bootloader
I (27) boot: compile time Nov 9 2026 12:13:18
I (27) boot: chip revision: v1.1
I (28) qio_mode: Enabling default flash chip QIO
I (33) boot.esp32: SPI Speed : 80MHz
I (36) boot.esp32: SPI Mode : QIO
I (40) boot.esp32: SPI Flash Size : 16MB
I (44) boot: Partition Table:
I (46) boot: ## Label Usage Type ST Offset Length
I (53) boot: 0 nvs WiFi data 01 02 00009000 00004000
I (59) boot: 1 otadata OTA data 01 00 0000d000 00002000
I (66) boot: 2 phy_init RF data 01 01 0000f000 00001000
I (72) boot: 3 launcher OTA app 00 10 00010000 00110000
I (79) boot: 4 retro-core OTA app 00 11 00120000 00100000
I (85) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=354e0h (218336) map
I (148) esp_image: segment 1: paddr=00045508 vaddr=3ffb0000 size=03f18h ( 16152) load
I (153) esp_image: segment 2: paddr=00049428 vaddr=40080000 size=06bf0h ( 27632) load
I (162) esp_image: segment 3: paddr=00050020 vaddr=400d0020 size=ba888h (764040) map
I (357) esp_image: segment 4: paddr=0010a8b0 vaddr=40086bf0 size=0d4fch ( 54524) load
I (374) esp_image: segment 5: paddr=00117db4 vaddr=500009f0 size=00020h ( 32) load
I (384) boot: Loaded app from partition at offset 0x10000
W (390) esp_psram: Virtual address not enough for PSRAM, map as much as we can. 4MB is mapped
E (402) spi_flash: Detected size(4096k) smaller than the size in the binary image header(16384k). Probe failed.
assert failed: 0x400d33c7
--- 0x400d33c7: __esp_system_init_fn_init_flash at D:/esp/v5.5.6/esp-idf/components/esp_system/startup_funcs.c:118(inlined by) __esp_system_init_fn_init_flash at D:/esp/v5.5.6/esp-idf/components/esp_system/startup_funcs.c:110
Backtrace: 0x4008b981:0x3ffe3b70 0x4008b949:0x3ffe3b90 0x40091cca:0x3ffe3bb0 0x400d33c7:0x3ffe3bf0 0x400d32ab:0x3ffe3c10 0x400d32de:0x3ffe3c30 0x40081222:0x3ffe3c60 0x40079872:0x3ffe3c90 |<-CORRUPTED
--- 0x4008b981: panic_abort at D:/esp/v5.5.6/esp-idf/components/esp_system/panic.c:489
--- 0x4008b949: esp_system_abort at D:/esp/v5.5.6/esp-idf/components/esp_system/port/esp_system_chip.c:87
--- 0x40091cca: __assert_func at D:/esp/v5.5.6/esp-idf/components/newlib/src/assert.c:40
--- 0x400d33c7: __esp_system_init_fn_init_flash at D:/esp/v5.5.6/esp-idf/components/esp_system/startup_funcs.c:118(inlined by) __esp_system_init_fn_init_flash at D:/esp/v5.5.6/esp-idf/components/esp_system/startup_funcs.c:110
--- 0x400d32ab: do_system_init_fn at D:/esp/v5.5.6/esp-idf/components/esp_system/startup.c:132
--- 0x400d32de: do_core_init at D:/esp/v5.5.6/esp-idf/components/esp_system/startup.c:170(inlined by) start_cpu0_default at D:/esp/v5.5.6/esp-idf/components/esp_system/startup.c:204
--- 0x40081222: call_start_cpu0 at D:/esp/v5.5.6/esp-idf/components/esp_system/port/cpu_start.c:988
--- 0x40079872: set_cache_and_start_app at D:/esp/v5.5.6/esp-idf/components/bootloader_support/src/bootloader_utility.c:1153(inlined by) unpack_load_app at D:/esp/v5.5.6/esp-idf/components/bootloader_support/src/bootloader_utility.c:888(inlined by) load_image at D:/esp/v5.5.6/esp-idf/components/bootloader_support/src/bootloader_utility.c:798
로그를 보니 PSRAM 이 충분치 않다고 하고 Flash의 초기화가 failed 되버립니다. 뭐가 문제지… 하고 한참을 헤멘 끝에 로그 초반에 보니 Flash Size 가 16 MB 라고 되어 있네요. 이상하네요 esplay-micro 로 선택을 했는데 말이지요.
결국 다음과 같이 수정을 좀 해줘야 합니다.
먼저 launcher 로 들어가서 “idf.py menuconfig” 를 실행합니다.


여기서 Flash 사이즈가 16 MB 로 되어 있는걸 4 MB 로 바꿔줍니다.
다음에 아래의 경로에서와 같이 esplay-micro 에서 sdkconfig 파일의 플래시 사이즈를 다시 수정해 줍니다.
git diff diff --git a/components/retro-go/targets/esplay-micro/sdkconfig b/components/retro-go/targets/esplay-micro/sdkconfig index a0c329d6..5ae616ad 100644 --- a/components/retro-go/targets/esplay-micro/sdkconfig +++ b/components/retro-go/targets/esplay-micro/sdkconfig @@ -16,10 +16,10 @@ CONFIG_ESPTOOLPY_FLASHFREQ_20M=n CONFIG_ESPTOOLPY_FLASHFREQ="80m" CONFIG_ESPTOOLPY_FLASHSIZE_1MB=n CONFIG_ESPTOOLPY_FLASHSIZE_2MB=n -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=n +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_ESPTOOLPY_FLASHSIZE_8MB=n -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=n +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
수정 후에는 기존에 생성했던 bin 파일들을 삭제하고 다시 빌드를 해줘야 합니다.
python rg_tool.py --target=esplay-micro clean launcher retro-core python rg_tool.py --target=esplay-micro build-img launcher retro-core prboom-go esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 "img 파일명"
이번에는 다시 빌드 할때 prboom-go 를 추가 했습니다. ( 4 MB를 넘지 않을 듯 해서 추가했습니다 )
Partitions:
[0]: type=0, subtype=16, size=1114112 (97% used), label=launcher
> WARNING: File larger than partition (+32256 bytes), increased size to 1114112
[1]: type=0, subtype=17, size=1048576 (97% used), label=retro-core
[2]: type=0, subtype=18, size=851968 (97% used), label=prboom-go
> WARNING: File larger than partition (+44080 bytes), increased size to 851968
Flash size: 2.938 MB
File 'retro-go_1.46-8-g4ced1-dirty_esplay-micro.img' successfully created.
All done!
네 다시 빌드가 완료되었습니다. 이번에는 2.9 MB 정도 되는군요. 이 정도 용량도 보드의 Flash에 충분히 들어갈 수 있습니다. 다시 esptool.py 로 다운로드를 시도해 봤더니…
esptool.py --chip esp32 --port COM5 write_flash --flash_size detect 0x0 retro-go_1.46-8-g4ced1-dirty_esplay-micro.img
esptool.py v4.12.0
Serial port COM5
Connecting.....
Chip is ESP32-D0WD (revision v1.1)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 08:b6:1f:ae:9e:8c
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash will be erased from 0x00000000 to 0x002f0fff...
Compressed 3080448 bytes to 1866934...
Wrote 3080448 bytes (1866934 compressed) at 0x00000000 in 164.8 seconds (effective 149.5 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
네 정상적으로 다운로드가 완료되었습니다. 그리고 게임 보드의 화면을 보니…

와우! 드디어 게임을 올리는데 성공했습니다. 제 보드에서 키도 잘 먹는거 같아 보이네요.
시험삼아 게임 롬 파일을 올려보고 게임을 해보기로 했습니다. 확인해 보니 SD 카드가 있으면 게임 보드가 부팅하면서 자동적으로 롬파일 폴더들을 생성해 주는군요. 여기에 제가 가지고 있는 롬파일 몇개를 넣고 게임이 되는지 간단하게 확인해 봤습니다.

핀볼 게임인데 잘 실행이 되는군요. 키도 잘 먹어서 정상적으로 동작을 잘 합니다. 일단 ESP32 게임보드에 소스를 빌드하여 게임기 동작 시키는거 성공입니다ㅎㅎㅎ
retro-go 소스에서 Flash 사이즈를 4 MB로 패치한 내역은 아래 저의 깃허브에서 적용하여 업데이트 하였습니다.
retro-go 를 그대로 포크하여 esplay-micro 의 플래시 사이즈만 4 MB로 변경한겁니다. 확인해보니 esplay-micro 를 “ESP32-WROVER-B (SoC 16MB Flash + 8MB PSRAM)” 를 사용한 거네요. 이걸 ESP32-WROVER 용으로 패치한거라고 보심 됩니다.
그럼 지금까지 ESP32 게임보드에 펌웨어를 빌드하여 올리는 과정에 대해 알아봤습니다. 처음에는 저도 잘 몰라서 ESPLAY의 깃허브를 따라가다가 많이 헤맸지만 retro-go 라는 소스를 통해 손쉽게 게임보드에 올리는 과정을 확인하였습니다.
이후에 다른 글에서 ESP32 게임보드의 게임들에 대해 좀더 자세히 올려보도록 하지요.

