Microcontrollers like the ESP32 are most often associated with smart home devices and general processing devices, but as a canvas for code, you can do a whole lot more than that. That’s why I decided to temporarily build an arcade machine with the Elecrow CrowPanel Advance 7-inch display... just because I could. It’s dreadfully simple, but that’s the point; these devices are easy to turn into anything that you want, and so long as you have the hardware, the sky is the limit. Oh, and now I can play Pong and Tetris.
All of this is enabled by a couple of key aspects of this particular display. Its large screen size is great,…
Microcontrollers like the ESP32 are most often associated with smart home devices and general processing devices, but as a canvas for code, you can do a whole lot more than that. That’s why I decided to temporarily build an arcade machine with the Elecrow CrowPanel Advance 7-inch display... just because I could. It’s dreadfully simple, but that’s the point; these devices are easy to turn into anything that you want, and so long as you have the hardware, the sky is the limit. Oh, and now I can play Pong and Tetris.
All of this is enabled by a couple of key aspects of this particular display. Its large screen size is great, but a frame requires a lot of RAM, and that’s why the 8MB of PSRAM alongside the powerful ESP32-S3 is so important. Not only that, but the ability to connect a speaker allows you to have audio to go along with it, too. It’s quite basic to use, but the fact this works as well as it does is immensely impressive to me, and I’m consistently surprised by the power of the ESP32.
Building an arcade machine with an ESP32
Easier than you might think
I first had the idea for this project before I even received this panel, but the practicality of the project was an uncertainty until I saw that someone got an NES emulator running on an ESP32. There are plenty of classic games that ran in tighter constraints than the ESP32 can give, so I figured that if those older devices could do it, then the ESP32 definitely can, and the NES emulator proved it. I looked for sample code for people who had built basic games on the ESP32 before, like Tetris and Pong, and worked on porting those over to this display.
Of course, I made a few changes and improvements, while also facing some bugs. Pong’s AI player is very rudimentary, and just calculates based on the height of the ball and where it is relative to the paddle, with the paddle moving at a defined speed towards the ball. The ball will increase in speed every 32 frames, using a bitwise operation as it’s more efficient than a modulo calculation.
The architecture of the program itself is fairly simple; I have a main file that serves as the entrypoint, establishing all of the hardware necessary for it to run. It then displays a menu, where I can choose to play Pong or Tetris. Tapping one will set a variable which changes the behavior of the main program loop, switching the device over to the chosen game. The game logic is defined separately and only comes into play here. At first, this worked fine, but the screen had intense flickering, and it was basically unusable. That’s why I ended up creating a double buffer, where a frame is drawn to the screen, the next frame is drawn to PSRAM, and then those are swapped and updated every second. It doubles the RAM requirement (to roughly 1.5MB), but it works.
However, something I didn’t expect and took a long time to wrap my head around was the fact that the screen was ever so slightly desynchronized during this process. Eventually, I worked out that my double buffer wasn’t the problem... kind of. First of all, here’s what we know about the buffer:
- One buffer of 800x480x2 is 750 KB per buffer
 - Two buffers, therefore, is 1.5 MB
 - A pixel clock of 18 MHz transfers at a speed of 36 MB/s
 
That 36 MB/s has to be sustained from PSRAM to the LCD DMA, while the CPU (and LovyanGFX, used for graphics) are also writing into the back buffer in PSRAM. Octal PSRAM should be capable of this, yet I discovered that dropping the pixel clock to 16 Hz (from 18 Hz) was enough to fix it with only mild, occasional region-specific flickers.
The problem stems from the fact that, at 800x480 with a pixel clock of 18 MHz, the RGB engine must pull 18 million pixels per second, and the ESP32-S3’s PSRAM and CPU share the same external memory bus. That means that while the RGB DMA is streaming pixels out of PSRAM, LovyanGFX is also trying to write new frame data into that same PSRAM.
Lowering the pixel clock gives DMA more time to finish PSRAM read cycles in time and maintain a stable, consistent connection to the LCD. Now, we have a seamless, no flicker double buffer that looks good and fits exactly what we need. A double buffer does add slight input lag, but for this project, that’s completely fine.
An ESP32 display is a great device to make a game for
It’s capable of a lot
If you have an ESP32 with a display, why not try make a game? It can be a fun and interesting project, and there are a lot of code samples out there. I’ve published my code for this over on GitHub so that you can check it out and learn from it, and maybe even play some pong if you have the same display.
All in all, this was an incredibly fun (though difficult) project, and I’m glad I was able to pull it off. Thankfully, there were many existing code samples out there that could help me get started, and it’s thanks to the open-source community that this was even possible. I thought about adding a joystick to physically control Pong, and while it would have likely worked, it took a long time to even get to the stage that I did thusfar.
Now if you’ll excuse me, I’m going to go play Pong against a terrible, janky AI at 20 FPS.