Maker.io main logo

Numpad 4000 Mechanical Keyswitch Data Entry Device

2021-12-14 | By Adafruit Industries

License: See Original Project 3D Printing Adafruit Feather RP2040

Courtesy of Adafruit

Guide by John Park

Overview

Compact keyboards are super cool -- I use a TKL -- but sometimes you just miss having a big, old number pad for data entry and calculations. The Numpad 4000 is just that! Plus, you can move it off to either side of your main keyboard for ideal ergonomics.

You can build the custom mechanical Number Pad of your Dreams with the Ortho NeoKey Snap-Apart PCB, a Feather RP2040, and CircuitPython. Customize the physical layout on this diode-matrixed, NeoPixel-lit wonder board!

Parts

This project should work well on nearly any CircuitPython-capable Feather board. I chose to use the very lovely Feather RP2040.

Key Switches and Keycaps

The NeoKey Ortho 5x6 PCB uses up to 30 keyswitches and key caps, although for the Numpad 4000 you'll only need 22.

Keycaps

To build the Numpad 4000 as designed here you'll need a set of number pad-specific keys. I used SA profile, double-shot Maxkey keycaps -- these numberpad keys happened to be left over from a TKL (ten key-less) build I did. Talk to a friend who has ordered some fancy keycaps before -- in many cases they'll have left over numpad keys as people tend to make smaller boards!

Or, go for a fully orthographic build using 1u keycaps such as these DSA profile ones:

Tools and Materials

You'll need a 3D printer and filament to build the switch plate and case. Alternately, you can make a variant stacked design using acrylic or wood on a laser cutter or mill, or even with chipboard and a CNC cutter such as the Cricut.

You'll also need a soldering iron, solder, and thin wire such as this:

  • Silicone Cover Stranded-Core Wire - 30AWG in Various Colors

Numpad Layout

layout_2

Not all keycaps are square. Keycap ratios are expressed as proportional widths relative to a "one unit", or, "1u" standard, which you find on most of the "normal" alpha-numeric keys on a keyboard.

Other key sizes exist: modifiers are usually 125% width, or 1.25u. Here are some common sizes (these may vary with different designs):

  • "normal" keys = 1u

  • alt, ctrl = 1.25u

  • tab = 1.5u

  • caps lock = 1.75u

  • numpad 0, +, enter = 2u

  • spacebar = 6.25u

Here's a nice resource for learning more about keyboard anatomy, Keyboard University.

anatomy_3

Getting Off the Grid

Since this design uses a few 2u keycaps, we'll need to move some of the NeoKey snap-apart PCBs off of the default 1u grid.

The NeoKey Ortho 6x5 Snap-Apart PCB makes it easy to rearrange the layout.

To keep things neat and stable, you can design a key plate for 3D printing, laser cutting, or CNC milling.

An excellent tool for designing your layout in the browser is the Keyboard Layout Editor.

Keyboard Layout Editor

An excellent tool for designing your layout in the browser is the Keyboard Layout Editor.

You can start a new, blank layout, then add keys and rearrange their sizes and positions using the per-key controls.

editor_4

Using the Keyboard Layout Editor, I created the layout I want.

create_5

Once the layout is set, I headed to the Raw Data tab to copy the markup text shown here:

Download File

Copy Code
[{c:"#606cc4",t:"#ffffff",p:"DSA",a:5,f:4},"NUM\n\n\n\n\n\nLOCK",{a:7,f:6},"DEL",{f:9},"/","*","-"],
[{a:5,f:4},"PAGE\n\n\n\n\n\nUP",{c:"#408dff",a:7,f:9},"7","8","9",{c:"#606cc4",h:2,_s:0},"+"],
[{a:5,f:3},"PAGE\n\n\n\n\n\nDOWN",{c:"#408dff",a:7,f:9},"4","5","6"],
[{c:"#606cc4",f:6},"FN",{c:"#408dff",f:9},"1","2","3",{c:"#606cc4",f:3,h:2,_s:0},"ENTER"],
["CTRL",{c:"#408dff",f:9,w:2,_s:0},"0","."]

Highlight and copy the markup text so you can use it in the next step.

Note, I added the _s:0 code to remove stabilizer cutouts from the 2u switches.

Plate & Case Builder

Now that you have the keyboard laid out, you can use the swillkb Plate & Case Builder to generate the CAD drawings of your plate layout.

Paste the markup text from the Raw Data you copied in the Keyboard Layout Editor in to the Plate Layout field. Pick and options you want, and then click the Draw My CAD!!! button.

button_6

Pick the CAD Output tab and you'll see your drawing.

CAD_7

Click on the file type button to download an SVG, DXF, or EPS file of the drawing. You'll use this in your 3D modeling package of choice, or to generate a laser cutter or CNC toolpath.

numpad4k.svg

Model

Most 3D modeling and CAD programs will allow you to import the drawing file of your choice. I used the venerable .dxf file and imported it into Rhino/Grasshopper.

I then created a Grasshopper workflow to effectively extrude the collection of curves 1.6mm in height to generate the switch plate model. (I also had it offset the outer boundary curve by 2mm and fillet the corners.)

The 1.6mm plate height is a good starting point, allowing the switches to click into place nicely, but you can try making it thicker if you like.

Model_8

Export the model as an .stl file and 3D print it! (Note, the print shown here was made using the notched keyswitch profile, but the squares work just as well or better due to increased connection surface.)

export_9

Assemble the Numpad

Snap

The first step is to snap off the top and bottom of the PCB.

Use some pliers to gently bend at the perforation until the extra material snaps off.

snap_10

snap_11

Please use eye protection when breaking apart printed circuit boards.

breaking_12

Extra Column No More

Pry off the extra column at the far right of the board, as seen from the top side of the PCB.

You can save these key PCBs for use in another project later, such as a five-key macro strip!

PCB13

PCB14

Snip Snip

Use diagonal cutters to cut off the four key PCBs of the fifth column as shown, taking care to leave the first PCB in place.

Then, snap off two of the four key PCBs you just removed and reserve them for use with the + and ENTER keys later in the build.

You will eventually wire and solder these loose PCBs back into the numpad matrix.

snip_15

snip_16

snip_17a

More Snip Snip

Repeat the previous process to turn the bottom row of four into a spaced-out row of three that will accommodate the 2u spacing of the 0 key.

spacing_17

spacing_18

spacing_19

spacing_20

This is the fundamental numpad layout you'll be working with. Next, we'll clean up the board edges.

edges_21

PCBs contain fiberglass which is hazardous to your health. Wear a facemask when filing or sanding the edges.

Edge Cleanup

Use your diagonal cutters to remove some of the excess material where the boards were separated, then file them down with a small metal file or sandpaper.

Be sure not to breathe in the dust!

dust_22

dust_23

dust_24

3D Case

Use the files linked below and print the plate, top, base, and bottom parts.

3Dcase_25

Numpad 4000 Case Files

Keyswitches, Plate, PCBs

Arrange the PCBs as shown, then snap a few keyswitches into place.

Be careful to align the two metal legs of the keyswitch with the sockets of the PCBs.

metal_26

metal_27

metal_28

metal_29

One Offs

For the 2u spaced keys, snap the keyswitches through the plate, then press the PCBs on from the back.

one_30

one_31

one_32

one_33

one_34

Wiring

It's time to wire it all up! There are two sets of wiring tasks here -- one is to connect the key matrix column and row pins to the Feather RP2040 as well as the power, ground, and NeoPixel pin. The other wiring task is to re-connect the snapped-off key PCBs to the others.

Note how the NeoPixel data line runs in a snake-like pattern through the grid.

Follow this wiring diagram to make the connections.

wirediagram_35

connection_36

Here the plate and keyswitches have been removed to expose the wiring more clearly.

removed_37

Note: Sometimes the PCB traces that run between the key PCBs can become damaged when neighboring PCBs have been cut, just due to strain on the thin connection. You can repair these with a short jumper wire as shown on the third PCB on the bottom row in the following photograph.

traces_38

Assembly

Now that you've wired everything, you can insert all of the keyswitches, and then add the keycaps.

assembly_39

assembly_40

assembly_41

assembly_42

assembly_43

Case Middle

Feed the Feather RP2040 through the case middle section as shown.

case_44

case_45

case_46

case_47

case_48

Case Top

Place the case top on top of the boards so you can sandwich everything and screw the parts together.

You can start with the three long screws.

top_49

top_50

top_51

top_52

Feather Screws, Standoffs

Add the four shorter screws to the Feather mounting holes.

Then, thread the seven hex standoffs onto the screws.

featherscrews_53

featherscrews_54

featherscrews_55

featherscrews_56

Case Bottom

Set the case bottom in place and then use the smallest screws to attach it to the hex standoffs from below.

You can also add rubber bumper feet as shown for a non-skid experience.

bottom_59

bottom_58

bottom_57

bottom_60

bottom_61

The Numpad 4000 is assembled and ready for coding in CircuitPython.

Install 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.

Download the latest version of CircuitPython for this board via circuitpython.org

Click the link above to download the latest CircuitPython UF2 file.

Save it wherever is convenient for you.

click_62

Boot_63

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 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.

drag_64

drag_65

The RPI-RP2 drive will disappear and a new disk drive called CIRCUITPY will appear.

That's it, you're done! :)

drive_66

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.

Entering Safe Mode in CircuitPython 6.x

This section explains entering safe mode on CircuitPython 6.x.

safe_67

To enter safe mode when using CircuitPython 6.x, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 700ms. On some boards, the onboard status LED (highlighted in green above) will turn solid yellow during this time. If you press reset during that 700ms, 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.)

Entering Safe Mode in CircuitPython 7.x

This section explains entering safe mode on CircuitPython 7.x.

To enter safe mode when using CircuitPython 7.x, 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

Once you've entered safe mode successfully in CircuitPython 6.x, the LED will pulse yellow.

If you successfully enter safe mode on CircuitPython 7.x, the LED will intermittently blink yellow three times.

If you connect to the serial console, you'll find the following message.

Copy Code
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 doesn't even show up as a disk drive when installing CircuitPython, try loading this 'nuke' UF2 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.

Download flash erasing "nuke" UF2

Code and use the Numpad 4000

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, along with a folder full of key configuration files. To get everything you need, click on the Download Project Bundle link below, and uncompress the .zip file.

Drag the contents of the uncompressed bundle directory onto your Feather board's CIRCUITPY drive, replacing any existing files or directories with the same names, and adding any new ones that are necessary.

Download Project Bundle

Copy Code
# SPDX-FileCopyrightText: 2021 John Park for Adafruit Industries
# SPDX-License-Identifier: MIT
# NUMPAD 4000! Made with snap-apart NeoKey PCB and Feather RP2040.

import board
import keypad
import neopixel
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

COLUMNS = 5
ROWS = 5

BLUE = 0x000510
WHITE = 0x303030
RED = 0xFF0000


board_pix = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.1)
board_pix[0] = BLUE

key_pixels = neopixel.NeoPixel(board.D5, 30, brightness=0.1)
key_pixels.fill(WHITE)

keys = keypad.KeyMatrix(
    row_pins=(board.D4, board.A3, board.A2, board.A1, board.A0),
    column_pins=(board.D13, board.D12, board.D11, board.D10, board.D9),
    columns_to_anodes=False,
)

kbd = Keyboard(usb_hid.devices)

keycode_LUT = [
             0, 1, 2, 3, 4,
             5, 6, 7, 8,
             10, 11, 12, 13, 14,
             15, 16, 17, 18,
             20, 21, 23, 24
]

pixel_LUT = [
             0, 1, 2, 3, 4,
             8, 7, 6, 5,
             10, 11, 12, 13, 14,
             18, 17, 16, 15,
             20, 21, 23, 24
]
# create a keycode dictionary including modifier state and keycodes
keymap = {
            (0): (0, Keycode.KEYPAD_NUMLOCK),
            (1): (0, Keycode.BACKSPACE),
            (2): (0, Keycode.FORWARD_SLASH),
            (3): (0, Keycode.KEYPAD_ASTERISK),
            (4): (0, Keycode.KEYPAD_MINUS),

            (5): (0, Keycode.PAGE_UP),
            (6): (0, Keycode.KEYPAD_SEVEN),
            (7): (0, Keycode.KEYPAD_EIGHT),
            (8): (0, Keycode.KEYPAD_NINE),

            (9): (0, Keycode.PAGE_DOWN),
            (10): (0, Keycode.KEYPAD_FOUR),
            (11): (0, Keycode.KEYPAD_FIVE),
            (12): (0, Keycode.KEYPAD_SIX),
            (13): (0, Keycode.KEYPAD_PLUS),

            (14): (1, Keycode.SHIFT),
            (15): (0, Keycode.KEYPAD_ONE),
            (16): (0, Keycode.KEYPAD_TWO),
            (17): (0, Keycode.KEYPAD_THREE),

            (18): (2, Keycode.CONTROL),
            (19): (0, Keycode.KEYPAD_ZERO),
            (20): (0, Keycode.KEYPAD_PERIOD),
            (21): (0, Keycode.KEYPAD_EQUALS)  # KEYPAD_ENTER on non-mac
}

shift_mod = False
ctrl_mod = False


while True:

    key_event = keys.events.get()
    if key_event:
        if key_event.pressed:
            if keymap[keycode_LUT.index(key_event.key_number)][0] == 1:
                shift_mod = True
            elif keymap[keycode_LUT.index(key_event.key_number)][0] == 2:
                ctrl_mod = True
            if shift_mod is False and ctrl_mod is False:
                kbd.press(keymap[keycode_LUT.index(key_event.key_number)][1])
                print(keymap[keycode_LUT.index(key_event.key_number)][1])
                key_pixels[pixel_LUT.index(key_event.key_number)] = RED
            elif shift_mod is True and ctrl_mod is False:
                kbd.press(Keycode.SHIFT, keymap[keycode_LUT.index(key_event.key_number)][1])
                print(keymap[keycode_LUT.index(key_event.key_number)][1])
                key_pixels[pixel_LUT.index(key_event.key_number)] = RED
            elif shift_mod is False and ctrl_mod is True:
                kbd.press(Keycode.CONTROL, keymap[keycode_LUT.index(key_event.key_number)][1])
                print(keymap[keycode_LUT.index(key_event.key_number)][1])
                key_pixels[pixel_LUT.index(key_event.key_number)] = RED
            elif shift_mod is True and ctrl_mod is True:
                kbd.press(
                          Keycode.SHIFT,
                          Keycode.CONTROL,
                          keymap[keycode_LUT.index(key_event.key_number)][1]
                          )
                print(keymap[keycode_LUT.index(key_event.key_number)][1])
                key_pixels[pixel_LUT.index(key_event.key_number)] = RED
            board_pix[0] = WHITE

        if key_event.released:
            if keymap[keycode_LUT.index(key_event.key_number)][0] == 1:  # un-shift
                shift_mod = False
            elif keymap[keycode_LUT.index(key_event.key_number)][0] == 2:  # un-ctrl
                ctrl_mod = False

            kbd.release(keymap[keycode_LUT.index(key_event.key_number)][1])
            key_pixels[pixel_LUT.index(key_event.key_number)] = WHITE
            board_pix[0] = BLUE

View on GitHub

Use the Numpad 4000

Once the libraries and code are installed, the Numpad 4000 will work as a USB HID keyboard device. You can try it out right away by plugging the keyboard into your computer via a known good USB cable and then typing in some numbers and symbols.

This guide page has a great intro to CircuitPython HID Keyboard.

For even more details, check out the documentation at https://circuitpython.readthedocs.io/projects/hid/en/latest/ which includes all of the keycodes and media codes you can use.

If you want to customize the keys, to send different keycodes, this dictionary contains all of the mappings:

Download File

Copy Code
keymap = {
            (0): (0, Keycode.KEYPAD_NUMLOCK),
            (1): (0, Keycode.BACKSPACE),
            (2): (0, Keycode.FORWARD_SLASH),
            (3): (0, Keycode.KEYPAD_ASTERISK),
            (4): (0, Keycode.KEYPAD_MINUS),

            (5): (0, Keycode.PAGE_UP),
            (6): (0, Keycode.KEYPAD_SEVEN),
            (7): (0, Keycode.KEYPAD_EIGHT),
            (8): (0, Keycode.KEYPAD_NINE),

            (9): (0, Keycode.PAGE_DOWN),
            (10): (0, Keycode.KEYPAD_FOUR),
            (11): (0, Keycode.KEYPAD_FIVE),
            (12): (0, Keycode.KEYPAD_SIX),
            (13): (0, Keycode.KEYPAD_PLUS),

            (14): (1, Keycode.SHIFT),
            (15): (0, Keycode.KEYPAD_ONE),
            (16): (0, Keycode.KEYPAD_TWO),
            (17): (0, Keycode.KEYPAD_THREE),

            (18): (2, Keycode.CONTROL),
            (19): (0, Keycode.KEYPAD_ZERO),
            (20): (0, Keycode.KEYPAD_PERIOD),
            (21): (0, Keycode.KEYPAD_EQUALS)  # KEYPAD_ENTER on non-mac
}
制造商零件编号 5157
NEOKEY 5X6 ORTHO SNAP-APART MECH
Adafruit Industries LLC
制造商零件编号 4884
ADAFRUIT FEATHER RP2040
Adafruit Industries LLC
制造商零件编号 5149
KAILH MECHANICAL KEY SWITCHES -
Adafruit Industries LLC
制造商零件编号 4955
KAILH MECH KEY SW 1=PACK OF 10
Adafruit Industries LLC
制造商零件编号 4997
BLACK DSA KEYCAPS FOR MX COMPATI
Adafruit Industries LLC
制造商零件编号 5031
CABLE A PLUG TO C PLUG R/A 3.28'
Adafruit Industries LLC
制造商零件编号 3299
BLACK NYLON SCREW AND STAND-OFF
Adafruit Industries LLC
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum