I have created several projects that involve various flavors of the classic game, Pong. It’s quite simple, as it’s comprised of just two paddles and a ball that can be hit around a screen.
But I wanted to do something different; I wanted to make a game that didn’t require a screen at all — but could instead be played on discrete NeoPixel LEDs. By doing this, a large, yet low-power display could be formed and run many varieties of games.
First and foremost, there must be a way to view the game while it’s being played, which is why I went with 48 LEDs laid out in an 8 by 6 grid.
They are attached to a capacitor which smooths out the power going to the LEDs, resulting in less interference and more stability. The microcontroller I chose was the ATmega8A, which is a small, cheap MCU that has plenty of RAM and speed to run the game. It also has abundant GPIO pins for interfacing with the buttons.
I began the PCB design phase by first making a schematic, which shows the components used and how they interface with each other. The ATmega8A is connected to a 16MHz oscillator so it doesn’t have to rely on its internal 8MHz one, resulting in a drastic speed increase. The voltage regulator takes in a 9v input and knocks it down to 5v for the MCU and LEDs.
The WS2812b LEDs are connected in a cascading series, where each LED is connected to the previous one’s data output pin into its data input pin.
The PCB is quite simple, as it contains the logic and supporting components on one side (the MCU, reset button, etc.) and the LEDs and buttons on the other.
I did this to make the LED side cleaner to view, as unsightly components would be hidden underneath.
I would like to thank pcbgogo.com for providing the PCBs to me.
The game of Pong is quite simple: two paddles hit a ball back and forth until the ball goes offscreen. Although these rules aren’t that complex, the implementation can be very difficult. First, an instance of the Adafruit_NeoPixel class is created and called ‘pixels’, which will handle all neopixel-related functions. Next, default values are placed into their respective variables, such as the positions for the paddles and ball, along with the colors for each. Because the NeoPixels were not placed in order on the PCB (a bit of an oversight), I created a lookup table that can be placed into EEPROM that maps each pixel’s actual location to its virtual one.
In order to maintain adequate speed, delays are non-blocking, as the millis() function is used in conjunction with variables that measure the time between updates. For instance, the code has a definition called FRAME_DELAY with a value of 40ms, which means that whenever millis() returns a value that is >= 40, a new frame is drawn. Frames are composed of the paddles and the ball, which are displayed by using the Adafruit_NeoPixel::setPixelColor() function and calling pixels.show() after.
Collision checking happens every 20ms by default. Three things are checked: the ball intersecting player 1’s paddle, the ball intersecting player 2’s paddle, or the ball hitting a wall. If the ball goes past a player’s paddle, then the opponent gains a point, the scores get displayed, and the game is reset for an additional round.Playing the Game and Future Plans
Playing Pong on a NeoPixel PCB is quite fun against an opponent. In the future, I would like to add more games, such as Snake or simple Tetris. I also plan on designing a 3D-printable case that can be used to support the PCB and shield the components underneath.