Overview
Play DOOM directly from the flash memory of a KB2040 embedded INSIDE THE KEYBOARD!
Press the custom DOOM button to launch!
A secret USB hub mounted inside the keyboard does the trick of allowing both the keyboard and the Adafruit KB2040 KeeBoar connect to your Windows computer over a single USB cable. CircuitPython watches the NeoKey button and launches a tiny ZDoom.exe when pressed. The host computer doesn't need anything installed at all!
DoooooOOOOM
Mechanical Keyboard
A keyboard with enough space inside to tuck away the hub and microcontroller, plus an added special key. Bonus points for embedded trackpad for all-in-one emergency use.
I used the Cherry Touchboard which is available from DigiKey
Page last edited October 04, 2024
Text editor powered by tinymce.
Build the DOOM Keeb
The DOOM Keeb will only have a single USB cable to plug into your computer. That cable runs to the CH334F mini USB hub's upstream port.
The keyboard's built-in controller will be plugged into one of the hub's downstream ports, and the KB2040 the other.
The KB2040 will have a NeoKey breakout plugged into it so that it can launch the ZDoom.exe file that's stored on the KB2040's flash memory when pressed.
Disconnect Keyboard Cable
The USB cable is plugged into the keyboard controller with a small connector.
Carefully unplug.
Cut the Cord
Cut the original cord about four inches from the interior connector -- we'll reuse that in a moment.
Strip some insulation from the USB cable as shown.
USB Cable to Hub
Strip and tin the four USB wires.
Solder these to the upstream port connections on the hub (the set of pads opposite of the USB C jack):
- Red to HOST PORT 5V
- Black to HOST PORT GND
- Green to HOST PORT D+
- White to HOST PORT D-
You've now got a two-port USB hub embedded in your keyboard case!
Keyboard Connection Cable
Remove the outer insulation and shielding from the keyboard cable you cut earlier.
Strip and tin the ends and then solder them to the mini USB hub's downstream port 1 as shown.
- black to USB hub PORT1 GND
- red to USB hub PORT1 5V
- green to USB hub PORT1 D+
- white to USB hub PORT1 D-
The white connector can be plugged into the keyboard's controller once we're ready for testing.
NeoKey Wiring
Cut an 8" length each of four wires for the NeoKey to connect to the KB2040.
Strip and tin the wires, then solder to the NeoKey and the KB2040:
- black from NeoKey - to KB2040 GND
- red from NeoKey + to KB2040 3V
- blue from NeoKey S+ to KB2040 A1
- yellow from NeoKey IN (NeoPixel In) to KB2040 A2
Also solder a short black wire from NeoKey S- to NeoKey - (this is what sends the switch to ground when it is in the unpressed state).
KB2040 Wiring
Cut an 5" length each of four wires for the KB2040 to connect to the hub's downstream port 2 connections.
Strip and tin the wires, then solder:
- black from KB2040 GND to USB hub PORT2 GND
- red from KB2040 RAW (5V) to USB hub PORT2 5V
- green from KB2040 D+ to USB hub PORT2 D+
- white from KB2040 D- to USB hub PORT2 D-
Connection Test
Plug the which keyboard driver connection back in so the keyboard can be enumerated on the hub.
Plug the USB cable into your computer -- the three green LEDs will light up to show that the hub has power and the two devices plugged into it have enumerated.
The KB2040's green status LED will light up, and the NeoKey will light up red.
Case Mod
In order to add the DOOM launch key properly, you'll need to cut a hole in the case right under the existing ESC key.
Measure & Mark
Measure the ESC key hole and mark the same size just below it -- calipers can be handy for this.
Score, Drill, Cut, File
Use a utility knife and straight edge to score the lines a bit deeper than the initial scribe lines.
You can drill the corners, too, then continue to cut or use a coping saw to remove the rest of the material.
Use a small file to clean up the edges, and round over a bevel to match the existing edges.
Key Fitting
Use some thick double-stick tape or Uglu dots to place the key lightly in place.
Line up the key more precisely with the top of the case in place, then press the key more securely.
Remove the case top and use hot glue to more securely afix the NeoKey breakout to the keyboard PCB.
You can now close up the keyboard case fully and screw it back together.
Custom Keycap Cover
You can print a 1" square keycap cover and fold it to fit inside the re-legendable keycaps as shown.
Page last edited October 04, 2024
Text editor powered by tinymce.
CircuitPython
CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.
CircuitPython Quickstart
Follow this step-by-step to quickly get CircuitPython running on your board.
Click the link above to download the latest CircuitPython UF2 file.
Save it wherever is convenient for you.
To enter the bootloader, hold down the BOOT/BOOTSEL button (highlighted in red above), and while continuing to hold it (don't let go!), press and release the reset button (highlighted in red or blue above). Continue to hold the BOOT/BOOTSEL button until the RPI-RP2 drive appears!
If the drive does not appear, release all the buttons, and then repeat the process above.
You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.
A lot of people end up using charge-only USB cables and it is very frustrating! Make sure you have a USB cable you know is good for data sync.
You will see a new disk drive appear called RPI-RP2.
Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.
The RPI-RP2 drive will disappear and a new disk drive called CIRCUITPY will appear.
That's it, you're done! :)
Safe Mode
You want to edit your code.py or modify the files on your CIRCUITPY drive, but find that you can't. Perhaps your board has gotten into a state where CIRCUITPY is read-only. You may have turned off the CIRCUITPY drive altogether. Whatever the reason, safe mode can help.
Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode bypasses any code in boot.py (where you can set CIRCUITPY read-only or turn it off completely). Second, it does not run the code in code.py. And finally, it does not automatically soft-reload when data is written to the CIRCUITPY drive.
Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the CIRCUITPY drive.
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)
In Safe Mode
If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.
If you connect to the serial console, you'll find the following message.
Auto-reload is off. Running in safe mode! Not running saved code. CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode. Press any key to enter the REPL. Use CTRL-D to reload.
You can now edit the contents of the CIRCUITPY drive. Remember, your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode.
Flash Resetting UF2
If your board ever gets into a really weird state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on your Flash Memory. You will lose all the files on the board, but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.
Page last edited October 04, 2024
Text editor powered by tinymce.
Code the DOOM Keeb
Text Editor
Adafruit recommends using the Mu editor for editing your CircuitPython code. You can get more info in this guide.
Alternatively, you can use any text editor that saves simple text files
Download the Project Bundle
Your project will use a specific set of CircuitPython libraries, and the code.py file. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.
Plug your Kee Boar board into your computer with a known good USB cable with both data and power wires. It should show up as a thumb drive in your File Explorer or Finder (depending on your operating system) named CIRCUITPY.
Drag the contents of the uncompressed bundle directory onto your Key Boar board CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.
# SPDX-FileCopyrightText: 2024 John Park & Tyeth Gundry for Adafruit Industries
#
# SPDX-License-Identifier: MIT
'''
DOOM launch for Windows
'zdoom.exe' for Windows https://zdoom.org/downloads must be in CIRCUITPY/zdoom directory.
extract https://github.com/fragglet/squashware/releases squashware-1.1.zip, rename to 'doom1.wad',
place in same CIRCUITPY/zdoom directory as the .exe.
'''
import time
import board
from digitalio import DigitalInOut, Direction
import keypad
import neopixel
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
from adafruit_hid.keycode import Keycode
keys = keypad.Keys((board.A1,), value_when_pressed=False, pull=True) # set up NeoKey launch button
kbd = Keyboard(usb_hid.devices) # create keyboard object
layout = KeyboardLayoutUS(kbd)
led = DigitalInOut(board.D13) # on-board LED
led.direction = Direction.OUTPUT
led.value = True
pixel = neopixel.NeoPixel(board.A2, 1, auto_write=False, brightness=1.0) # NeoKey LED
pixel.fill(0x440000)
pixel.show()
def launch_terminal(): # function for launching local DOOM
kbd.send(Keycode.GUI, Keycode.R) # open run cmd
time.sleep(0.25)
# pylint: disable=line-too-long
layout.write("powershell -c \"& { gwmi win32_logicaldisk -f 'DriveType=2' | % { try { $p = $_.DeviceID + 'zdoom\\zdoom.exe'; if (Test-Path $p) { Start-Process $p; break } } catch {} } }\"")
time.sleep(0.25)
kbd.send(Keycode.ENTER)
time.sleep(2)
while True:
event = keys.events.get() # check for keypress
if event:
if event.pressed:
pixel.fill(0xff0000) # brighten LED
pixel.show()
launch_terminal() # launch DOOM
if event.released:
pixel.fill(0x440000) # dim LED
pixel.show()
import time import board from digitalio import DigitalInOut, Direction import keypad import neopixel import usb_hid from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.keycode import Keycode
Button and LED Setup
-
Button: Connected to pin
A1. This button will be used to trigger the DOOM launch. - Keyboard Object: Creates a virtual keyboard to send keystrokes to the PC.
- LED: On-board LED is turned on initially.
- NeoPixel: A single NeoPixel LED is used for visual feedback.
How the Code Works
When you press the button connected to board.A1, the script will use the virtual keyboard to open the Run dialog on a Windows PC, execute a PowerShell command to locate and launch zdoom.exe, and provide visual feedback through the NeoPixel LED. The LED turns bright red when the button is pressed, and dims when it is released.
Imports and Setup
-
time: For delays in the script. -
board: Provides access to board-specific pins. -
digitalio: For controlling digital input/output pins. -
keypad: To handle button presses. -
neopixel: To control the NeoPixel LED. -
usb_hid: For USB HID devices. -
adafruit_hid: Libraries for emulating keyboard input.
keys = keypad.Keys((board.A1,), value_when_pressed=False, pull=True) kbd = Keyboard(usb_hid.devices) layout = KeyboardLayoutUS(kbd) led = DigitalInOut(board.D13) led.direction = Direction.OUTPUT led.value = True pixel = neopixel.NeoPixel(board.A2, 1, auto_write=False, brightness=1.0) pixel.fill(0x440000) pixel.show()
Function to Launch DOOM
-
Open Run Dialog: Sends
GUI+R(Windows key + R) to open the Run dialog. -
Powershell Command: Uses PowerShell to search for the
zdoom.exefile in a specific directory (CIRCUITPY/zdoom). - Launch DOOM: If the file is found, it starts the DOOM executable.
def launch_terminal():
kbd.send(Keycode.GUI, Keycode.R)
time.sleep(0.25)
layout.write("powershell -c \"& { gwmi win32_logicaldisk -f 'DriveType=2' | % { try { $p = $_.DeviceID + 'zdoom\\zdoom.exe'; if (Test-Path $p) { Start-Process $p; break } } catch {} } }\"")
time.sleep(0.25)
kbd.send(Keycode.ENTER)
time.sleep(2)
Main Loop
- Check for Key Press: Continuously checks if the button is pressed.
- LED Feedback: Changes the NeoPixel color to indicate whether the button is pressed (bright red) or released (dim red).
-
Launch DOOM: Calls
launch_terminal()when the button is pressed.
while True:
event = keys.events.get()
if event:
if event.pressed:
pixel.fill(0xff0000)
pixel.show()
launch_terminal()
if event.released:
pixel.fill(0x440000)
pixel.show()
Page last edited October 04, 2024
Text editor powered by tinymce.
Play DOOM
To play DOOM, plug in the keyboard -- after a moment it and the CIRCUITPY drive will be recognized and the DOOM key will light up.
Press the DOOM key to launch, then just wait a few seconds for it to load.
Now you're ready to play!
Page last edited October 04, 2024
Text editor powered by tinymce.