ChipChamp is an open-source gaming platform built to increase the understanding of how hardware and software really works.
It is designed to be beginner friendly, affordable, and playful.
ChipChamp appears as a USB drive when connected to a computer and games can be programmed directly in CircuitPython with a text editor. I have made a package of basic games as inspiration and starting point for you to create new ones.
How ChipChamp differs from similar projects:
- Simple design – easy to build and understand.
- Beginner friendly to program (write CircuitPython code directly on the USB drive).
- Polyphonic audio (instead of a beeper).
- Built in internet connection and multiplayer support over Wi-Fi.
- Open source – hack and improve the design.
- Very affordable ($10) and using components that are easy to order. (no custom PCB)
- Quite powerful – it should be able to run Nintendo emulators and even DOOM (remains to be seen).
Specification
- Processor: ESP32-S2 processor with USB-OTG, 4MB Flash and 2MB PSRAM.
- Display: 160×128 pixel TFT with 65536 colors (ST7735 driver).
- Control: D PAD and A, B keys for input, using quiet switches with low activation force.
- Audio: 3.5mm audio socket connected to 4 channel PWM or 8-bit DAC. (not yet supported in CircuitPython)
- Possibility to add a smartwatch Li-ion battery to make it fully portable.
You can build your own device with a 3D printer, soldering iron and a handful of components. Everything is open source and hacking is encouraged!
Order the components
Before you can build it, you need to order the components. They are easy to obtain and can be bought from Amazon (faster) or AliExpress (cheaper). Prices below are from ordering 100 units from AliExpress 202304.
- 1x Lolin ESP32S2 with integrated PSRAM. (Be aware of knockoffs without PSRAM. Model number should be N4xx, not H4xx) ($2.1)
- 1x Display 160×128 pixels and ST7735 driver ($2.3)
- 6x Quiet mouse witches ($0.5)
- 1x TRRS Audio jack breakout board ($0.3)
- 1x USB-C cable (with data) ($0.6)
Optional components to make it portable:
- 1x Li-Ion smartwatch battery (LQ-S1 380mAh) ($0.6)
- 1x USB-C battery charger board ($0.3)
- 1x Schottky diode (BAT85 or similar) ($0.01)
You need to have access to the following equipment:
- 3D printer (fff) & PLA filament
- Soldering iron & solder
- Strands of copper wire
- Pilers
- Microscope (optional)
- Desoldering equipment (optional)
Build it
ChipChamp uses a 3DPCB that integrates components, wirechannels and case in one part, removing the need for a custom PCB. By manually connecting the components using copper wires, you get a hands-on understanding for how the components are connected and how the signal flows.
3D print
The 3DPCB can be printed on a FFF / FDM printer using standard PLA filament. Download the .stf file from github and slice it in your preferred slicer. The audio breakout board needs support, but the channels and other features do not. (Some slicers allow you to paint the areas that require support structures.)
I use the following settings:
- 0.4mm nozzle
- 0.2mm layer height
- 15% infill density (gyroid)
The total time is 2h15 and uses 22g or 7.3m filament (1.75mm)
To build the portable version with a LI-ion battery you will also need to print two more files (battery cover and a tiny connector), same settings as above.
Step by step building instructions:
The instructions below can be printed on a single page.
- Secure the Lolin module with a drop of hot glue (ensure that the holes are aligned)
- Insert the switches, fold the pins, and solder the first copper wire to the switch pin above.
- Route the wire through its channel and solder it in place on the microcontroller board.
- Tighten the wire and heat up the solder one more time to remove any slack.
- Solder wires to the other switches according to the drawing.
- Route the wires and solder the other end to the microcontroller.
- Cut the wires 1mm from the microcontroller board.
- Secure the audio jack with a drop of hot glue.
- Secure the battery raiser with quick-glue.
- Solder the common ground to audio, A/B Switches.
- Thread the remaining wire two turns around the battery terminal.
- Solder ground together with a new wire (LCD GND) to the microcontroller.
- Solder the other ground wire so it connects all switches and the microcontroller.
- Mount the resistors so they align with the holes and solder them to the microcontroller.
- Solder both audio pairs together where they meet.
- Connect both pairs with a wire and thread it through the hole and solder it to the audio jack.
- Solder the diode to the microcontroller and a wire to its other terminal. (ORIENTATION)
- Wind the diode wire around the battery terminal two turns.
- Solder remaining seven wires to the microcontroller and thread them through the first hole.
- Inspect all wires closely (any mistake will be difficult to fix once the LCD is mounted)
- Add adhesive tape on top of all wires under the LCD (keep LCD holes free).
- Secure LCD with hot glue to the adhesive.
- Thread remaining wires through the hole and LCD and solder them in place
- Cut the wires 1mm from the LCD
Install CircuitPython
The lolin ESP32 needs to have CircuitPython installed before we can write or add programs to it.
- Put the esp32 in download mode:
- Connect the ESP32 to a computer using the USB-C cable
- Press and hold the boot key and click the reset key (before the boot key is released)
- Download the latest CircuitPython binary for the lolin s2 mini platform.
- Flash the downloaded firmware at 0x0000 using the webflasher.
Write your first program
Connect the ESP32 to a computer using the USB_C cable. It should then appear as a drive like an USB memory. Edit the program.py file with your favorite editor. CircuitPython can send debug data over a Serial device using the same USB cable (REPL), so it is convenient to use a editor with built in serial connection. I prefer VSCode with the serial module, but mu is also a good choice.
Hello World
Let’s start with a simple “hello world” program. It is quite useless but demonstrate how a simple program is run. When you work with microprocessors that doesn’t have a display, the “hello world” message is replaced by flashing a LED.
Copy the following code (select it and press ctrl-c).
import hw
import time # module for delay
print("Hello World!") # Serial output, not screen
while True: # loop forever…
time.sleep(0.2) # 200ms delay
hw.builtinLed.value = not hw.builtinLed.value # Toggle
Copy hw.py (from github) to the \lib directory. Open code.py in your editor, replace it with the code above and save the file. When you write to the filesystem the python interpreter is reset and the code is executed.
The built in blue led on the lolin board should blink @ 5Hz and the serial terminal should say “Hello World”. If the LED flashes but the terminal doesn’t read “Hello world” – verify that you have the right serial port selected and that the baud rate is 115200 bps.
ESP32-S2
I choose the ESP32-S2 due to the USB-OTG, memory size, cost and internet connectivity. Early experiments included RP2040, ESP32 and Longan nano.
ESP32-S2 is a Wi-Fi enabled microcontroller developed by Espressif Systems. It is a follow up on the popular ESP32 microcontroller, and is designed to be used in IoT devices, smart home automation systems, industrial automation, etc.
It has an upgraded Xtensa single core 32-bit LX7 processor, running at 80MHz to 240 MHz. Compared to ESP32 it has integrated USB OTG, improved security features, but lacks Bluetooth.
The Lolin ESP32 S2 mini module is equipped with 320kB RAM, 4MB Flash and 2MB PSRAM. The module has a USB-C connector, built in power regulator, WiFi Antenna and 27 I/O connections providing ADCs, DACs, PWM, SPI, I2C, UART, Touch sensors, and more, making it highly versatile and suitable even for gaming devices ;)
How everything is connected
The peripheral devices (keys, display and sound) are connected to the microcontrollers GPIO (General Purpose Input Output) pins thru the wires you have soldered. Before a program can read the switches or write to the display, the GPIO needs to be configured, telling the processor how the signals are connected and if they are inputs or outputs.
Keys
The A, B and D-pad switches are connected between six separate inputs ground. This means that the input reads 0 when the key is pressed and 1 when it is released.
In order for the key to work, the microprocessor I/Os must be configured as inputs with weak pull-up resistors to stabilize the voltage when the switch is not pressed. This is done in common circuitPython libraries, but needs to be configured if you program in C.
Display
The display has an integrated ST7735 display driver that the microprocessor communicates with using a SPI serial interface where data (PICO /SDA/ MOSI) is send synchronously to the display using a high-speed clock signal (SCK / SCL).
Chip Select (CS) tells the display to listen to the SPI bus (allowing several devices to share clock and data). Data/ Command (DC), tells the display if the transferred byte is a command (instruction) or data (pixel color). Read the ST7735 datasheet if you want to have full control of the display (for 1337 users).
Audio
The audio interface is a bit untraditional in order for it to support two different working modes. One for minimal processor load, and one more traditional for full audio control. Four outputs are mixed using 10k resistors that also current limit the outputs and reduces volume to an acceptable level in most headphones. Left and right channels are combined (mono out), and note that they are not AC coupled if you connect them to an audio amplifier, since most amplifier inputs are…
PWM – Pulse width modulation
This method allows up to four channels (one for each output to generate polyphony) to output square wave signals with minor performance impact since the PWM generation is integrated and only needs to be updated when a tone starts, begins or changes, typically at 60Hz or less. The down side is lack of volume control and hence ADSR. This mode is suitable for old school square wave bit pop ;)
DAC – Digital Analog Converter
The ESP32S2 has two 8-bit DACs that can be updated for precise control of the sound. This allows all sorts of sounds and instruments to be generated, but requires much more CPU resources since the main program typically needs to be interrupted to update the DAC 44100 times per second. A tip is to use both DACs to reduce load on the output driver and ground one I/O while the other is raised in order to set “zero” to 1.65V. Use this mode if you want full freedom for the generated sound, can afford the performance impact and are comfortable with phase counters, ADSR and polyphonic synthesis (for 1337 users).…
In theory it should be possible to combine PWM with DMA playback of the DACs. This would allow samples to be mixed with the square wave channels, useful for sound effects or drums with almost no performance impact (to be verified…)
(There is currently no Circuit Python library for audio generation, only C code to demonstrate that the hardware is working)
Power
The power solution is quite straight forward. The integrated LDO on the LolinS2mini board provides 3V3 to the ESP32S2 and the display. The battery voltage can be up to 4V which The power solution is quite straight forward. The integrated LDO on the LolinS2mini board provides 3V3 to the ESP32S2 and the display. The battery voltage can be up to 4V which would damage the microcontroller, so it is connected to VBUS (USB 5V). The schottky diode (BAS85) prevents USB power to feed 5V to the battery which would damage it and could even cause it to explode…
The battery terminals are made by wounding the copper wire around the battery raiser. It is a simple solution, but far from ideal to have copper as battery terminal. If it will corrodes over time, remove the battery cover and polish the terminals.
Note that there is no power switch – simply pull the battery slightly to turn it off!
Download project files
All files required for this project are available at this GitHub repository: https://github.com/vonkonow/ChipChamp
License
This project is open source under MIT License so feel free to adapt, improve and share your result!
(Attribution is optional but appreciated /Johan von Konow ;)