Introduction: Battery-powered Wireless Tetris Console Pair

About: The opinions in my posts are personal and not necessarily shared by the company I work for. Sometimes I don't agree with myself either. PhD in Computer Science and Engineering.

I wanted to experiment with a 64x64 rgb led panel. So I decided to make a portable battery-powered console, with at least one game on it. Then I realized that game must be Tetris. Then I realized I could make a pair of consoles for multi-player games. Then I realized the ESP32 has a fantastic ESPNow protocol with low latency, no wifi needed (or better, it creates its own wifi), which fit the purpose. Then I realized what’s a console without some proper squeaky buzzer sound?? Then I realized by using ALL the pins on the ESP32 I could have 2 buzzers, for polyphonic music and effects. Then I realized I could design a 3d-print a nice case for them, which would also allow to place some ergonomics controls.

The result is the nerdiest project I’ve done so far, I hope you can appreciate the attention to the tiniest useless detail :)


Features:

  • portable (kinda. Already working on a shrunk version)
  • wireless 2 player gaming (mimicking Tetris on the GameBoy)
  • wireless OTA firmware update
  • ergonomic 6-buttons controls
  • 2-channel audio
  • battery powered (gentle on battery, exceeds 4 hours of gameplay)
  • implementing (almost) all Tetris guidelines

Supplies

  • 2x 64x64 P3 HUBT75 RGB panels (192x192mm). I’ve used the model from Waveshare, which kindly sponsored this project. Note these were my 4th and 5th rgb panels, I have multiple 32x32 and 64x64 panels, but these were by far the easiest to work with, starting from the dupont to 16 channel ribbon cable connector which was very handy for the arduino: found here
  • 2x LED Plexiglass A4. You need to cut these to size: found here
  • 2x ESP32 boards. I’ve used AZ-Delivery lolin32 lite, which unfortunately doesn’t have a 5v input pin, and I didn’t want to use a DC-DC convertor, so I ended up cutting a micro usb cable and using that to connect the esp to the battery pack. found here
  • 2x AA battery holders. I have the ones with switches (you need to solder all the connections), whose door doesn’t close very well, so I’ve added some gap in the back plate to allow for this little bulge: found here
  • 12x momentary push buttons. I have tried the little clicky ones, they were pleasantly clicky but hard to mount and to press. So I’ve reverted to these: found here
  • assorted resistors (optional). This is really personal taste. Without resistor, the buzzers are pretty loud and over-distorted, so I preferred to put some resistors to limit the current. Which resistor to use depends on the volume you want to achieve. I kept adding resistors in parallel (didn’t have a proper assortment of resistors) until the volume was a good tradeoff: found here
  • assorted dupont and/or electric cables (stereo speak cable is your friend)
  • assorted 3A terminal joints: found here
  • 8x AA batteries. I’ve used rechargeable ones, each being 1.25v approximately, bringing the total voltage to 5x approximately, perfect for the esp and the panel
  • unless you want to CNC the case, or build it out of wood, you’ll need access to a 3d printer, with a 0.25mm nozzle for the grid (optional).
  • optionally, M3 and M2.5 screws and possibly press-fit nuts for the case. Depending on your 3d printer tolerances, layer height, nozzle, temperature, and filament, simple friction may be enough with this design to keep everything in place.
  • lots and lots of solder

Step 1: 3D Printing

Easier said than done. Normally, I'd just say "3D print the attached stl files". This time it's not that simple. First, the grid separating and diffusing the 64x64=4096!! pixels is best printed with a 0.25mm nozzle. If you don't have one you have three options: 1) buy one, they are cheap. 2) print with a 0.4mm and hope for the best results. 3) get it printed by a shop (expensive).

Second, if you do print the grid with a 0.25mm nozzle, be ready for >24hours printing times. In the times of Bambulabs X1 Carbon, I won't be reporting anymore how long it took, as your mileage this time WILL vary!

Third, tolerances and layer height (I used 0.20mm) will have an impact on the friction between parts (which are all tight fit), and may mean you won't require screws, or may need some post-processing of the internal components to make sure they fit.

With all the above caveats, go ahead and print the attached files :)

Step 2: Circuit

Again, lots of headaches here. Normally, I'd say "connect this with that". Look at the fritzing schema, it's not that simple :) First, we are using ALL pins from the ESP32, except the ones related to power. Second, the ribbon cable coming from the Waveshare panel has a large number of pins to connect to the esp32 .. take your time..

Notes on the circuit:

  • the resistors on the buzzers are optional. Only put them if you want a weaker and cleaner sound, like I did. I used 2.5kOhm resistors (well, 4 10kOhm resistors in parallel).
  • the 6 push buttons are wired in a counter-intuitive way. 2 of them are wired together, they are the bottom controls, Right 3 and Left 3. You can press either to send the piece down. Second, to be able to use the buzzers, I needed to use pins 34 and 35 as input, as they are input only. They don't have internal pullup resistors, so I needed to put some. Therefore, the wiring of those two buttons looks different than the rest.
  • when looking at the schematic, note the 16 pin HUB75 is displayed with the notch on the left side (covered by the wires in the picture)
  • I didn't include the wires from the battery pack to the 4 pin DC connector on the panel for simplicity, and also didn't include the wires from the connector to the cut microusb cable powering the ESP32
  • the ESP32 is powered via usb, using a cut micro usb cable (yeah, like said above :) )
  • note the picture depicts a 4xAAA battery pack. I used 4xAA, there was no picture for 4xAA batteries.

Tips:

  • I ended up using lots of pre-cut and pre-soldered speaker wire. I used two wire lengths: 5cm (about 2 inches) and 15cm (about 6 inches). I believe I prepared 12 x 5cm and 6 x 15cm. In case that's not correct, you'll find out while you connect the wires
  • to make life (and hardware debugging) simpler, I've made extensive use of electrical terminals. Those are indeed optional, but they are very convenient and they allow to quickly find out what's going on, and to replace what needs to be replaced without de-soldering a bunch of wires.
  • I've bent all the contacts of the push buttons before pre-soldering them. You need to do it to fit the 3d printed case, and it's better to do it before soldering

Step 3: Code

Ok so this is interesting. Normally, I'd say "load the attached code on the ESP32" (aaahh .. didn't I use "normally" already??), but it's not that simple.

First, you need a few libraries:

  • ESP32-HUB75-MatrixPanel-I2S-DMA
  • esp_now
  • esp32fota
  • gfx_fonts
  • LittleFS

And, you'll need to modify them. Specifically, modify the pin definition of the ESP32-HUB75 with the attached file (to replace the file in your Arduino library folder), which will tell the library the exact pins matching the schematic above, plus it will specify this is a 64x64 panel

Moreover, you'll need to modify the gfx_fonts library by adding the attached font file in its directory.

Now, onto the code. Let's review some extract:


AUDIO

The code uses the dual core nature of the esp32, and so far it's the only code I've seen that does that in a practical setting (i.e., in an actual project). One core executes the game dynamics, the other one takes care of music and sound effects. Because we only have 2 buzzers (we run out of pins!) we need to sacrifice the "bass" voice of the music when we need to "beep". The logic is very simple, the core check for time passed and checks if it needs to advance either buzzer to the next note in the melody. I've recreated the melody of the Tetris theme myself, none of the versions I've found on the Internet worked nicely.


CONTROLS

The code uses 6 buttons in a 5 control configuration, meaning that Right 3 and Left 3 are mapped to the same pin. I had to swap two buttons in the software for one of the two boards, but then I've changed the wiring and I left the software button "swap" in the code. Finding it and fixing it is left to the reader as an exercise :)


WIRELESS COMMUNICATION

The code uses the espnow protocol for communicating between ESPs without attaching to a wifi. It's efficient, fast, elegant, and simple to use. In my code, I need to know who the master and who the slave are, which is hard-coded using the MAC addresses of the two ESP32 I'm using. Find yours and change them in the code. It doesn't matter which is which as long as you define two separate MAC addresses.


FOTA update

You'll need to modify wifi, password, and server url if you want to use this functionality. You'll also need to upload your compiled bin file and a json to your server. A detailed guide of how this works can be found here: https://github.com/chrisjoyce911/esp32FOTA


SETTINGS

The code saves two settings, i.e. whether you have music and audio on. They persist after poweroff. "How" you ask? But with LittleFS of course! A small file is created and kept up to date with these two settings. The first time you upload the code you'll need to go to the settings twice to create the partitions. You only need to do this once, unless you over-write the filesystem.


GAMEPLAY

This is the "simple" part. The game continuously loops and checks for button inputs, actions to perform, winning status etc. In a 2-player game (mimicking the Gameboy Tetris), the units also send their current height to each other. The height of the opponent is displayed by a vertical red bar between the game area and the next piece list. If you clear 2, 3, or 4 lines at the same time, you send 1, 2, or 3 lines of garbage to the opponent, which the opponent gets right before the next piece is drawn.


MISSING / FUTURE WORK

  • Adding the ghost piece functionality, though I've never liked it. It's the "projection" of the current piece on the floor so you know where it will drop
  • Adding the "stashed" piece functionality, which would require an additional pin (could separate the two bottom buttons)

Step 4: Cut the Plexiglass (Acrylic)

If you've sourced the Plexiglass from my same source, you only need 1 cut per sheet, as the three remaining dimensions match my design already (one may say this was by design..).

Clamp the plexiglass as close as possible to the cut and as close as possible to the edge of a table. Put a guide between the plexiglass and the clamps. In the picture here you see the other plexiglass sheet used as a guide. Not the best idea, but it worked.

If you follow me, you know I've been cutting plexiglass before, using mostly the oscillating multi-tool with different blades. However now I've bought a very sharp knife (from Prusa's website), and I wanted to try to do it without the multitool. After 25-30 passes with the knife, I was able to put pressure and to get a perfectly clean cut with no residues.

Nevertheless, a bit of sanding here and there on the sides was necessary. A good suggestion is also to give a little smoothing to the 4 corners, so as to let them fit more easily.

Remember to proceed incrementally, you can always sand more, but you can't turn back.

Step 5: Assemble the Console

The video starts with a sped-up montage of the assembly.

In brief, once the electronics are completed, you need to:

  1. take the outer shell and place it with the front bezel down
  2. push-fit the cut acrylic (keep rotating and pressing, DON'T put too much pressure on a single area of the acrylic or it will break)
  3. push-fit the grid. If the grid has an elephant foot on one side, put that facing outwards, so the panel sits better on the larger holes
  4. put the panel, making sure to align the holes with the leds.
  5. put the middle internal 3d printed support frame
  6. make sure at some point you screw-in the battery pack on the back of the back panel
  7. press the back panel carefully reserving space for all the electronics in between. Everything should fit easily. IF it doesn't, something is stuck, reopen , check, and restart
  8. evaluate if and where you need screws. You may not need any. One of the two consoles in my case didn't require screws except for the battery case.
Battery-Powered Contest

Participated in the
Battery-Powered Contest