Home Archives About
moefh.github.io
The Magic Smoke Has Left the Building
Porting Loser Corps to the ESP32 - Part 4

More progress in porting Loser Corps to the ESP32 (see all posts about the port).

Working on the game network code, I was able to make two players (each running their own game a separate ESP32) see each other's characters:

First player (using the Arduino Joystick)
First player (using the Arduino Joystick)
Second player (using the Wiimote).
Second player (using the Wiimote).

They're using ESP-NOW (a protocol made by Espressif, the makers of ESP32) to communicate. Currently code is very simple, it's really just a proof-of-concept. Each ESP32 keeps broadcasting messages containing its character's position and frame. When it receives a message, it puts a second character at the received location with the received frame to represent the remote player.

The network part was much easier than expected, I got it running a couple of hours after I was able to get WiFi running inside the game (I had researched how to send and receive messages using ESP-NOW prior to that). The hard part was freeing enough memory to get the WiFi system to start in the first place. If you look closely at the monitors in the photos, you can see that the image's borders are cut at the sides: the drawing area is only 240x240 pixels (as opposed to the usual 320x240). The monitor still thinks it's using the old resolution, in effect what I did was increase the horizontal front- and back-porch of the VGA timing, which decreases the horizontal resolution but keeps everything else unchanged (crucially, the hsync timing).

That took care of the RAM, but we also had a ROM problem: with the added WiFi library, the program binary itself ended up too big to fit the flash (at least with the default Arduino IDE settings). So I removed some sprites, an entire tileset (castle) and cut some tiles from another tileset (castle3). To do that I made the conv_spr tool (in the conv_img directory in the Github repository) -- it's an improvement of the tool I had made before but hadn't released.

The nice side of all that work is that now it's much easier to change the resolution with the new VGA code. It can also select between using double framebuffers (as before) or a single framebuffer. I made that when researching ways to decrease VGA memory use -- it turns out that the game almost runs well enough with a single framebuffer, but it's not quite fast enough to draw the whole screen during vblank (the time when the monitor is not actively drawing the image), so the top portion of the screen gets a little messed up if we try. That's why the solution I chose at the end was to decrease the resolution and keep using two framebuffers, but I kept the code to use a single framebuffer in case I need it for another project in the future.

Homemade ESP32 VGA Boards

To start experimenting with the networking code of the ESP32 game engine, I made a new board for the ESP32 so I can have two of them running the game and talking to each other. The new board was supposed to be just a copy of the original one, but I discovered a silly mistake I made in the original wiring: the red-high and red-low wires were reversed. This caused quite a mess in the colors, I'm surprised I didn't notice it before:

Comparison showing that the old board has wrong red levels
Comparison showing that the old board has wrong red levels
Zoom in the
Zoom in the "P"
Bridge and torch
Bridge and torch
Zoom in the torch
Zoom in the torch

The "new dac" label refers to the fixed DACs I wrote about in the last post. I'm not sure I can see any difference between the old an new DACs, to be honest (my phone camera doesn't help -- its automatic color balance messes up the colors way more than my DACs, apparently).

In any case, it's easy to see the difference between the old and new boards. Not only the new board's images have a blue-greenish tint overall (less red), we can also see that the red gradient in the "P" is clearly fixed -- what should be black->dark red->medium red->bright red was wrongly shown as black->medium red->dark red->bright red, which is happens when the low and high bits of the red component are swapped:

correct gradientswapped first and second bits
binary00 01 10 1100 10 01 11
decimal0 1 2 30 2 1 3

Of course, since we can select the ESP32 output pins any way we want, it's easy to fix this in software by simply changing the pin assignments in the pin_config array in vga_game.ino: just swap PIN_RED_LOW and PIN_RED_HIGH. That's good news for me because I don't feel like making a third board right now.

And finally, just for reference, here are all of the homemade boards I'm using in this project:

New board
New board
Old board (notice that the red wires don't cross, unlike the blue and green ones)
Old board (notice that the red wires don't cross, unlike the blue and green ones)
DACs
DACs
Porting Loser Corps to the ESP32 - Part 3

More progress in porting Loser Corps to the ESP32 (see all posts about the port).

Even though there's nothing really exciting to report, this is a very long post because there's a lot of small changes:

  • Wiimote support
  • New VGA DACs
  • Rewriting the VGA output code
  • New font

Wiimote Support

The game now supports the Wiimote controller (via Bluetooth) using takeru's Wiimote library. I had to make a very small change to use it: I added a void * parameter to the callback function, so it can update the state of the controller object without forcing it to refer to the global binding.

The library works perfectly, the only slightly annoying thing is that I haven't figured out how to permanently pair the controller with the ESP32, so every time the ESP32 restarts you have to press the Wiimote buttons 1 and 2 at the same time to make the Bluetooth connection. Not a big deal, but it could be better.

New VGA DACs

I made a new VGA connector with new digital-to-analog converters (DACs). The old ones are slightly wonky because I didn't have resistors with good values when I made it, so I finally got some new resistors and made a new one:

New VGA connector with DACs
New VGA connector with DACs

For comparison, here's the old one:

Old VGA connector with DACs
Old VGA connector with DACs

To see why the new DACs are better than the old ones, we have to talk about how the VGA color signal is generated. The monitor wants to receive each color component (Red, Green, Blue) in their own wire, with a voltage between 0V and 0.7V indicating its intensity. To produce an image, the voltage has to be changed as the monitor sweeps the screen to draw the pixels: each pixel is drawn with the RGB intensity corresponding to the voltage of the red, green and blue wires at the time it's being drawn.

The ESP32 only has 2 internal DAC outputs, so we can't use them to create the 3 voltages we need for the RGB wires (even if it had 3, its DACs aren't fast enough to generate the signal we need at over 12MHz). So we make the ESP32 output each color component as multiple digital pins: in our case, we use 2 pins per component. Since a digital pin has two logical values (0 and 1), each component has 4 possible combinations (00, 01, 10, 11), and the DAC has to convert these to 4 voltages between 0V and 0.7V. Ideally, we'd like to map them like this:

pin values (from ESP32)intensityoutput voltage (to VGA monitor)
000%0V
0133%0.23V
1067%0.47V
11100%0.7V

The ESP32 output pins give 3.3V when high (logical 1) and 0V when low (logical 0), so we can build a small circuit connecting them to resistors to generate the desired output voltages:

DAC schematic (for each color component)
DAC schematic (for each color component)

That's the DAC. We just have to choose values for R1 and R2 so that when Pin1 and Pin2 are high/low we have the desired voltage in output as shown in the table above. (By the way, the 75Ω resistor is there for impedance matching with the monitor -- I don't think it's really right, but I don't know how to match the impedance properly).

The old DAC had resistor values of 1KΩ and 560Ω, which is what I had available at the time. The new DAC has 1KΩ and 470Ω, which outputs voltages closer to what we want:

DAC output voltages (dotted lines show theoretical best)
DAC output voltages (dotted lines show theoretical best)

The dotted lines show the theoretical best values for each pin value except for 00, which has the trivial-to-generate voltage level of 0V. The new DAC is overall closer to the theoretical best, but most importantly, it has more consistent spacing between the levels, while the old DAC has the interval between 01 and 10 smaller then the other ones.

I'm not sure the image result is that different, but for the record here's a photo of the game running with the new DACs:

Game screen with the new DACs
Game screen with the new DACs

Rewriting the VGA Output Code

I ended up rewriting the VGA output code. I was using bitluni's excellent ESP32Lib, but that library does a lot of stuff we don't need. That makes the code larger and also more complicated: when reading the code, I had trouble following exactly how things are done with the code written in C++ with multiple inheritance.

So to make sure I understood every detail, I ended up rewriting the code in plain C style. My code supports only double-buffered output, and changing the resolution requires altering the appropriate constants by hand (I might make it easier to change it if I end up using this code for something else). It also doesn't have any drawing API, all drawing must be done by writing directly to the framebuffer -- which suits the game just fine, because I was already doing that with ESP32Lib.

My only improvement over ESP32Lib was making the I2S system trigger an interrupt at the end of each frame (the interrupy simply increments a frame counter). With that, the main game loop can now wait for the end of the frame to swap the framebuffers and start drawing again (basically a vsync). That removes the annoying horizontal line from the screen which happens when the image is updated while it's being drawn, and also forces a constant framerate (that's why that photo above shows 60fps).

It's still possible to disable the vsync (returning to the old variable framerate and annoying horizontal line), which I do occasionally to check the performance of the game.

New Font

Since I'm not using ESP32Lib anymore, I had to make my own font and font-rendering code (which is pretty unoptimized, but for now it doesn't matter since I'm only using text for debugging). I used Krita to draw the font because it's easier to draw pixel-by-pixel with it than with Gimp.

Here's an image of the 6x8 font:

The new font
The new font

(The yellow/greenish background is what I used when drawing the font.)

Next Steps

I'm not sure what I'll do next: probably either sound or network. For network, I haven't even decided if I'll use ESP32's "ESP-NOW" protocol or normal WiFi/IP, but it will be fun to investigate the pros and cons of each one.