ChipChamp

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.

In many ways ChipChamp is a retro dream console like the Pico-8, only in physical form ;)

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.

Some games I have made (but you should make your own ;)

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).
Compact and easy to carry
Tetris implementation in python

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.
Lolin S2mini – ChipChamp’s brain

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)

Write a simple game with a few lines of code.

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.

  1. Secure the Lolin module with a drop of hot glue (ensure that the holes are aligned)
  2. Insert the switches, fold the pins, and solder the first copper wire to the switch pin above.
  3. Route the wire through its channel and solder it in place on the microcontroller board.
  4. Tighten the wire and heat up the solder one more time to remove any slack.
  1. Solder wires to the other switches according to the drawing.
  2. Route the wires and solder the other end to the microcontroller.
  3. Cut the wires 1mm from the microcontroller board.
  4. Secure the audio jack with a drop of hot glue.
  1. Secure the battery raiser with quick-glue.
  2. Solder the common ground to audio, A/B Switches.
  3. Thread the remaining wire two turns around the battery terminal.
  4. Solder ground together with a new wire (LCD GND) to the microcontroller.
  5. Solder the other ground wire so it connects all switches and the microcontroller.
  1. Mount the resistors so they align with the holes and solder them to the microcontroller.
  2. Solder both audio pairs together where they meet.
  3. Connect both pairs with a wire and thread it through the hole and solder it to the audio jack.
  1. Solder the diode to the microcontroller and a wire to its other terminal. (ORIENTATION)
  2. Wind the diode wire around the battery terminal two turns.
  3. Solder remaining seven wires to the microcontroller and thread them through the first hole.
  4. Inspect all wires closely (any mistake will be difficult to fix once the LCD is mounted)
All wires are mounted. Time to cover with adhesive tape and mount the LCD.
  1. Add adhesive tape on top of all wires under the LCD (keep LCD holes free).
  2. Secure LCD with hot glue to the adhesive.
  3. Thread remaining wires through the hole and LCD and solder them in place
  4. 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.

  1. 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)
  2. Download the latest CircuitPython binary for the lolin s2 mini platform.
  3. 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

ChipChamp schematic (x-ray mode)

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 ;)

3DPCB Keyboard

Description

This custom keyboard was created to speed up my 3D CAD development. My 60 most used commands are now just a click or two away. The keyboard is fully customizable, uses standard components and is easy to replicate since it does not use a PCB, instead everything is 3D printed using the 3DPCB idea.

Interactive webGL model of a section of the keyboard

Features

  • 33 keys (5×6+3)
  • Easy to customize (printable keycaps with laminated paper inlay)
  • Anyone can build it using a 3D-printer (FFF/FDM), common components and a soldering iron.
  • QMK firmware (allowing advanced keyboard layout)
  • Replaceable keycaps – twist and release
  • Using common 6x6mm TACT switches
  • Anti-ghosting using 1N4148 diodes
  • No PCB required (everything is 3D printed)
  • Low activation force (depending on switches)
  • Ergonomic – half size keyboard enables shoulder width position of hands.

Bill of Materials (BOM)

  • 1x Arduino pro micro (or clone)
  • 33x 6x6mm tact switches (low actuation force)
  • 33x 1N4148 Diodes (can be omitted, with risk of key ghosting when pressing multiple keys)
  • Strands of 0.3mm copper wire (from RK Cable)
  • PLA for 3D printer
  • Micro USB cable

Development

I realized that I frequently use the same commands when I am designing different things (like e.g. this project ;) in Rhino 3D. I tested custom keyboard shortcuts and tried to memorize them, but without much logic it was difficult to learn if revolve was ctrl+shift+7 or ctrl+alt+7 etc. With a custom keyboard, I could design the keycaps to show the most frequent operations. As my right hand is controlling the mouse, I wanted the keyboard to be left-handed and only half the size of a normal keyboard. This allowing the hands to operate closer to each other which results in a more ergonomic working position.  I started to take notes on which operations and keys that I used most frequently. Then I grouped them in different categories, like select, curves, surfaces etc. I started with two different functions per key, but realized that three operations were more logical for some keys.

I did my homework and studied tons of different keyboard solutions, both off-the-shelf solutions, but mainly custom mechanical DIY keyboards. I decided to position the keys in an ortholinear layout, allowing the fingers to move up and down instead of the common staggered layout. It improves the visual appearance and some say that it is better ergonomically.

With an estimate of how many keys I needed, and the number of operations per key, I started to design the keyboard. By now I have built several projects using the 3DPCB design, and a keyboard is perfectly suited for this technique. I started to design the keys around standard Gateron/ Kailh/ Cherry MX mechanical switches and did a single key prototype print.

First version using mechanical switches. Too high for this project, but the 3DPCB concept fits great with a mechanical keyboard. Maybe one day…
Note the space for embedded diode and the different height of the channels for copper wire, avoiding short circuit without the need for insulated / enamelled wire or PCB.

However, it became obvious that I preferred a keyboard with lower travel length, less build height and tactical feedback (clicky). I therefore switched to standard 6x6mm ALPS switches (I had previously bought a decent amount of 50g /0.5N versions for the 3DPCBoy) and did a one key prototype.

Building a good keyboard from scratch is quite difficult and it is hard to get the distinct click. Luckily, I have designed a few professional mobile phones for Sony Ericsson and Huawei, so I know most pitfalls to avoid. The challenge is to adapt the high precision multi-material keyboards with a 3D printed at home version. I am too embarrassed to mention number of iterations I did, but in the end, it was all worth it…

Routing and 3DPCB design

Working in Rhino 3D (my favorite surface modeler) I placed the switches in a 7×5 array with 19x19mm cc (almost the standard 19.05×19.05mm used by most keyboards). Since I wanted the keyboard to be as flat as possible, I decided to sacrifice two keys and replaced them with an Arduino pro micro. The columns were directly connected to the switches and routed on the backside, while each key got one 1N4148 diode (to prevent ghosting) before connecting them to the rows – routed on the top. By adding holes for each row, the wires were brought to the backside and connected to the Arduino. First version was ok but it was simplified in an iterative approach until I was satisfied with the routing.

2D drawing showing all components and the wiring. Bottom wires are magenta, top layer blue.

I added radiuses and trimmed the curves before I swept diamond shaped 3DPCB channels on the backside and added channels and groves for the front side wires, components and solder connections.

Backside of the 3DPCB base.

Front of the 3DPCB base.

Keymat design

I like simple designs, so I experimented with using a laminated paper overlay as keys, but realized that it dampens the z-movement too much. I then added some cutouts in the paper, but realized that the paper still did not move freely. I increased the slot width, but was still not satisfied with the tactile click – it became flimsy, and with holes that would gather dirt. I finally scrapped the paper idea and went for a 3D printed keymat using 2D springs. This was more promising, but highlighted the need for tuning. If I made the springs too wide, there was little area left for attaching the keys, and as expected, the thickness has a great impact on the tactile feedback as the stiffness of the spring follows the b*h^3/12. Only a tenth of a mm in cube will affect the tactile feedback. After several nights of spring designs and many, many test prints, I thought that I had the perfect design. When I multiplied it to the 7×5 grid, I realized that all the different radiuses created thousands and thousands of polygons that caused a large file size and slowed down real time rendering.

Initial design of keymat / 2d spring with radiuses

I simplified the design and replaced the radiuses with chamfers, resulting in a much lighter model.

Keytop design

In the first version the keys were printed upside down, with guiding pins for the keymat that was glued to the keys. It worked but prevented more complex key shapes. I then flipped the key, printing it with the bottom down and added a standard “+” attachment of the keys on the keymat. Unfortunately, it is difficult to get a press fit with the limited precision of the 3D printer. To avoid having to use glue, I thought of using a slide-in design, but realized that only a tenth of mm misalignment of the keys would cause it to scrape against the frame – again affecting the tactile feedback. I instead decided to use a bayonet mount, which allowed replaceable keys by lifting and twisting them 45 deg, with good x-y positioning and low build height. Again, I had to iterate more than a dozen times until I was happy with the result. The final version also has a spring that clicks it into position, preventing rotation that would cause scraping.

Evolution of keymat and keytop. For this project I used the keytop with white inlay. The curved tops feel great but requires laser etching, engraving or UV inkjet to mark the keys…
Rendering showing keymat and base with one keytop (without inlay)
Rendering with all parts (base with components, keymat, keytops and inlays that can be printed on a standard laser printer)

For my CAD version, I used key tops with a small border that allows a standard laser printout to be placed in the middle. However, I also designed a more traditional, curved key top that allows better distinction between the keys.

Key symbols

I started to use text for each key, but realized that with up to three functions per key, it became way too much information for the brain to quickly process. Instead, I replicated the CAD operation symbols from Rhino with simple lines. The key symbols were then exported to Illustrator where I played around with color schemes and added cutout marks for auto aligning the cameo CNC knife.

I printed the paper with a standard color laser printer, laminated both sides and placed a thin double-sided adhesive on the back (without removing the liner). A CNC knife was used to cut out all the keys with high precision. This can of course be done by hand, but would be a bit tedious. You can also try using a laser cutter, but I would expect burned edges.

3D Printing

Printing is quite straight forward, I printed everything using white PLA and standard parameters:

  • 100% infill
  • No support
  • 205 deg C

I used a BCN3D with a 0.4mm nozzle and printed the base first, then keymat and finally keytops, allowing me to assemble the components while the rest was being printed.

Freshly baked – some stringing due to an old nozzle.

Assembly

Once the printing is done you are ready for the assembly. I started with the switches and noticed that I had to straighten the legs before they could be inserted in the base. Once in position, I folded the legs so they secured the position. I then added the diodes and bent both legs, trimmed the length and soldered them to the switch. The anode (without the stripe) of the diode should be connected to the switch (I actually did the opposite, it is easy to change in QMK, but is not the default ). Now it’s time for the Arduino pro micro and the wires. I used 0.3mm strands of copper wire (0.07mm2 area) that I got from a stripped piece of RK-wire (the multi strained cable used for internal wiring in electrical 230V cabinets). Solder one end to the pro micro, thread it through the diamond shaped 3DPCB channels and then to each switch / diode until the channel ends. Inspect all components and joints and ensure that nothing is missing.

Add the keycaps to the keymat one by one, if they are not moving freely, trim the edges with a file or sandpaper. Then remove the liner from the double sided adhesive on the laminated inlays and attach them to the keycaps.

Program the firmware with QMK (see section below) and when you have confirmed that all switches are working – use 6 pins from a 0.1″ pin header to join and press fit the keymat with the base, then trim excess pin on the backside (you can see the 6 dots between the keys on the photo below).

Backside of the keyboard, showing how the switches are connected with the diodes and the column wires.
Frontside of the keyboard (without keymat and keytops), showing how the diodes are embedded in the design. Note that the standard direction of the diodes are the opposite (but can easily be configured in QMK)

Here is the description for row and column mapping to the arduino pro micro:

  • R0 (top)                PD7        6             a7          
  • R1          PE6        7            
  • R2          PB1        15           sclk
  • R3          PB2        16           mosi
  • R4          PB6        10           a10
  • C0 (left) PD4        4             a6
  • C1          PC6        5
  • C2          PF6         19           a1
  • C3          PB5        9             a9
  • C4          PF7         18           a0
  • C5          PB4        8             a8
  • C6          PB3        14           miso

QMK firmware

I selected QMK for the pro micro firmware. It is easy to use, allows tons of adjustments and is used in several custom keyboard builds. It is possible to install QMK locally, but I used two web-based tools:

I created three layers that are selected by the 2nd and 3rd key. Keys like “esc” and “del” are available in all layers. The CAD specific keys are mapped against custom shortcuts generated in Rhino. For instance:

  • Layer 0 -> Ctrl+0 -> _Scale
  • Layer 1 -> Ctrl+1 -> _Scale2D
  • Layer 2 -> Ctrl+2 -> _Scale1D

Kbfirmware.com then generates the firmware to the Arduino pro micro that was programmed using Arduino command line tools (copied from verbose output during upload)

Documents

Summary

I love the fact that you can design, print and assemble a custom keyboard that is very close to a commercial one. I have used the keyboard a lot and even if I am planning minor changes of the layout, it works better than expected. The biggest problem now is that I only have one – I guess I have to build a second keyboard (one at home and one for work).

This project is yet another great utilization of the 3DPCB idea. It is quick and easy to print, assemble and customize, and the final result is a sturdy keyboard without a PCB and without a rat’s nest of point-to-point soldering and different layers of insulation, which are commonly used in hand-built keyboards. I hope that this project is inspiring the custom keyboard scene, and that more will build keyboards with 3D printed circuit boards.

I’m impressed by the ease and power of QMK. I have already received requests for custom gaming keyboards for Fortnite and Minecraft. It is super easy to adapt the keyboard by changing the number of rows and columns. Or maybe I will design a version with linear/mechanical switches, possibly combining linear for movement and tactile for inventory…

Licensing

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.