Revision Zero of the Monticello Drive is a hand-wired keyboard installed in to a 3d printed baseplate designed to be used on my desk while I shake out some minor revisions, configure the keymap, adjust fingers, etc... eventually I'll make a PCB for the keyboard, but this is a good way to prototype it while you "dial in" the placement of your fingers and wrists, make changes, add remove keys, etc.
I've typed on this thing for a few weeks now, and it's mostly held up. The plastic base bowed a little bit with the switches installed, but when it's placed on my desk it's on a soft mousepad-like gamer mat so it doesn't wobble or anything like that. It doesn't even really bow when I type on it. Getting the prototype "switch biscuits" assembled and working reliably was enough of a pain that I wouldn't actually recommend them. making a hand-wire grid is a pain in the ass too, but it just accelerated my design work and the eventual production of The Monticello Drive Revision One
INPROGRESS fix this doc up to be self-contained for the 3D printed board project
Constructing a 3D printed Hand Wired Keyboard from nothin'
I've been on the MechanicalKeyboards subreddit long enough to know that folks' "my first custom keyboard" can be these ridiculously well-engineered things with custom PCBs and things like encoders and milled aluminum housings; it was always somewhat intimidating to build my own.
I've never designed my own PCB or had a case milled, but I have a soldering iron and a 3d printer, and with these we can build a keyboard:
BoM
nice!nano v2 or similar arduino pro micro compatible mechboards.co.uk
optional oled display mechboards.co.uk
2x25 switch biscuits mechboards.co.uk tindie
1N4148 surface mount diodes (switch biscuit does not support through-hole diodes!)
hotswap sockets mechboards.co.uk
MX compatible switches
26AWG hookup wire and the tools to cut and trim and tin and solder them all together
Constructions
Clone this document's repository to your local system and work inside of it.
modify/generate an Ergogen YAML configuration file. You probably just want to change the
staggersettings to be more comfortable to your hands, but you could make a completely custom keyboard here if you can get past the built-in assumptions at the top of this document (48 keys, ergo but single PCB, etc) There are some guidances and links to check out if you want to make more aggressive changes to the layout, though all the assumptions in the firmware and keymap helpers and what not will also fall out of date if you do.nix run .#ergogen -- -o generated -d to generate the
points.yamlfile (and PCB files and whatnot along the way!)nix run .#skgen will generate the
points.scadfile which is used to place the switch cutouts on a 3D printed plate in another scad fileadjust bounds of the baseplate in scad/atreoid-inputs.scad if necessary; you'll want to open
openscadand use its auto-reload feature or just work with the files directly in there, though you'll need to add some files to the OPENSCAD_PATH like the =nix build .#stls= output and theopenscad.nixstuff I found and repurposed. you can just clone BOSL2 to a local file and make it work, i'm sorry this isn't easier.nix build .#stls to generate
stlfiles for printing three STL files.print three parts: a left hand, a right hand, and a center with the mount for the nice!nano and the OLED display
I print on an Ender 5 Pro, but any printer with at least an 8 inch square bed should be able to handle it.
solder 50x "switch biscuit" PCBs with hot-swap sockets, diodes and perhaps LEDs if you feel particularly brave.
Now assemble the switch matrix.
socket keyswitches in to the printed baseplates first
then put the switch biscuits on the switches, measure out your jumper lengths using a pair of digital calipers
cut and strip and tin and solder those all in to two grids, one for each hand.
If you want to have LED lighting, you'll have to solder 3x as many jumpers to the biscuits, this can be miserable, godspeed, you!
wire the rows of each hand together and then to the MCU, wire the columns to the MCU. consult the under-view photo of the rev0 below.
nix build -L .#firmware to generate a
uf2file in theresultdirectory, =nix run .#flash= and force the MCU to bootloader mode to install it.it might be less trouble to just mount the bootloader's fake FAT partition and copy it yourself, that flash script can be finicky.
verify that everything is wired up, maybe you have to adjust the overlay's IO mapping in
kscan0if you wire it up differently.use 1.75mm filament to secure the hinges if your printer is well-calibrated or use some of the jumper wires like I did if it's not.
validate that your key matrix works, and then solder the OLED display to it. I use the three GPIOs that aren't on the edges of the board for the OLED rather than the default SCL/SDA pins, ZMK pin mapping is explored below.
INPROGRESS SCAD work
NEXT detangle the scad source
for now you can see it on this document's repository.
Desktop Atreoid build
Need to cut like one hundred short wires to the right lengths to work with the atreoid layout above.
ZMK configuration
likely will need to split this in to rev0, rev1, etc but for now here's one keeb:
The shield module is in ./zmk-config and should be detangled below.
The metadata file for the keyboard shield makes sure that zmk knows that it needs to build the keyboard.
file_format: "1"
id: monticello_drive
name: Monticello Drive
type: shield
url: https://arcology.garden/my/atreoid
requires: [pro_mciro]
exposes:
- i2c_oled
features:
- keys
- displaysome magic bullshit lol, configuring the OLED and basic stuff no one should need to touch:
config SHIELD_MONTICELLO_DRIVE
def_bool $(shields_list_contains,monticello_drive)if SHIELD_MONTICELLO_DRIVE
config ZMK_KEYBOARD_NAME
default "Ryan's Atreoid"
if ZMK_DISPLAY
config ZMK_DISPLAY
select LV_USE_CONT
select LV_FONT_MONTSERRAT_26
select LV_FONT_MONTSERRAT_16
select LV_USE_LABEL
select LV_USE_IMG
config I2C
default y
config SSD1306
default y
config SSD1306_REVERSE_MODE
bool
default n
endif # ZMK_DISPLAY
if LVGL
config LV_Z_VDB_SIZE
default 64
config LV_DPI_DEF
int
default 148
config LV_Z_BITS_PER_PIXEL
default 1
choice LV_COLOR_DEPTH
default LV_COLOR_DEPTH_1
endchoice
endif # LVGL
endifThe keyboard is configured in a fairly straightforward, if baroque manner. these devicetree files are a format that is easy to use to merge multiple trees of configuration, to reach in to the tree to re-configure things, etc and then generate code based on that tree... it's kind of like how NixOS constructs its config attrset merging a bunch of files together to construct an entire configuration and then build a derivation out to each dependency, down to the root system.
Usually you just need to configure the pin mapping in the matrix you've wired up. I also define a transformation matrix so that the left hand and right hand are defined on their own rows for legibility.
This makes the error messages coming out of the gen_defines.py script that zmk uses to generate C files from these device trees much, but it allows for much more legible documents, and the errors you get if you make a syntax error in the keymap file are already a pain to parse.
#include <dt-bindings/zmk/matrix_transform.h>
/ {
chosen {
zephyr,display = &oled;
zmk,kscan = &kscan0;
zmk,matrix-transform = &atreoid_scan;
};
kscan0: kscan_0 {
compatible = "zmk,kscan-gpio-matrix";
diode-direction = "col2row";
wakeup-source;
col-gpios
= <&pro_micro 21 GPIO_ACTIVE_HIGH>
, <&pro_micro 20 GPIO_ACTIVE_HIGH>
, <&pro_micro 19 GPIO_ACTIVE_HIGH>
, <&pro_micro 18 GPIO_ACTIVE_HIGH>
, <&pro_micro 15 GPIO_ACTIVE_HIGH>
, <&pro_micro 14 GPIO_ACTIVE_HIGH>
, <&pro_micro 16 GPIO_ACTIVE_HIGH>
, <&pro_micro 6 GPIO_ACTIVE_HIGH>
, <&pro_micro 5 GPIO_ACTIVE_HIGH>
, <&pro_micro 4 GPIO_ACTIVE_HIGH>
, <&pro_micro 3 GPIO_ACTIVE_HIGH>
, <&pro_micro 2 GPIO_ACTIVE_HIGH>
, <&pro_micro 0 GPIO_ACTIVE_HIGH>
, <&pro_micro 1 GPIO_ACTIVE_HIGH>
;
row-gpios
= <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
};
atreoid_scan: matrix_transform {
compatible = "zmk,matrix-transform";
rows = <4>;
columns = <14>;
map = <
// left hand
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6)
RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6)
RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6)
// right hand
RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13)
RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13)
RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,12)
RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11)
>;
};
};The shield definition also includes some magic to configure the OLED display with the i2c pins on non-standard pins; I use two of three in the middle of the PCB rather than the standard arduino i2c pins.
&pro_micro_i2c {
status = "okay";
oled: ssd1306@3c {
compatible = "solomon,ssd1306fb";
reg = <0x3c>;
width = <128>;
height = <32>;
segment-offset = <0>;
page-offset = <0>;
display-offset = <0>;
multiplex-ratio = <31>;
segment-remap;
com-invdir;
com-sequential;
inversion-on;
prechargep = <0x22>;
};
};
// remember that when I build a PCB version of this that i had to swap SDA and SCL on the board!
&pinctrl {
i2c0_default: i2c0_default {
group1 {
psels =
<NRF_PSEL(TWIM_SDA, 1, 7)>,
<NRF_PSEL(TWIM_SCL, 1, 2)>;
};
};
i2c0_sleep: i2c0_sleep {
group1 {
psels =
<NRF_PSEL(TWIM_SDA, 1, 7)>,
<NRF_PSEL(TWIM_SCL, 1, 2)>;
low-power-enable;
};
};
};DONE i should make a little jig to make it easier to measure and fold these for easy soldering
customize this https://www.printables.com/model/675415-wire-jumper-jigs/files:
print those
DONE and then cut them
2mm over measurements in the table above will give me plenty of space to strip and bend the wires to right angles
DONE solder the biscuits in to a grid
DONE wire up to a nice!nano
DONE OLED display
i have some of these lying around that i can use; ill need to modify the baseplate to add holes for the headers to come through
DONE repair the switch biscuits so that this thing is a reliable keyboard
CANCELLED wire up the trackpoint
need another digital pin, maybe... might wait for a bit since the prototype will be on my desk with the trackball.
CANCELLED RGB LEDs
these might end up being hard to solder on to the current switch biscuits, theyre awfully crowded now, but i have the LEDs to solder in. ill probably want them for layer notification eventually.