How to Build a Custom Split Mechanical Keyboard with Arduino and 3D‑Printed Parts
This tutorial walks you through designing, wiring, coding, and 3D‑printing a split mechanical keyboard using an Arduino Leonardo, matrix scanning, diodes for key‑ghosting prevention, and custom keycaps, providing a complete DIY solution for programmers seeking ergonomic, programmable keyboards.
Introduction
As a programmer, having a good keyboard feels essential, and a mechanical keyboard can greatly improve coding comfort and personal style.
Standard mechanical keyboards
Using a regular keyboard for long periods can cause wrist fatigue because the wrist stays bent while typing.
Ergonomic split keyboards are often expensive and use membrane switches, which is not ideal for a budget‑conscious developer.
Ergonomic “halo” keyboards
To avoid the high price, the author decided to DIY a split mechanical keyboard, printing the case with a 3D printer and using an Arduino Leonardo as the controller.
Preparation
Tools
3D printer (for printing the case)
Soldering iron
Hot‑glue (to fix keycaps)
Multimeter
Hardware
Arduino Leonardo board
Black mechanical switches (80 pcs)
Diodes, resistors, jumper wires, prototype board
Small screws for assembling the case
Software
SolidWorks (or any 3D‑modeling software) for case design
Arduino IDE for writing the keyboard firmware
Steps
Design the key layout in SolidWorks, create a support panel for the switches, design the circuit, solder the matrix, write the firmware, test each key, design the full case, and finally assemble everything.
Key layout design
The right‑hand half is slightly twisted because the printable area of the 3D printer is limited.
Circuit design
Because the Arduino has limited I/O pins, a matrix‑scanning method is used: n scan pins and m receive pins form an n×m matrix. All scan pins are held low by default; each is set high in turn to detect which key is pressed.
The Arduino Leonardo provides 6 analog and 14 digital pins. Using 8 digital pins for scanning and 6 analog + 4 digital pins for receiving yields an 8×10 matrix that supports up to 80 keys, leaving two spare digital pins for custom functions.
Key‑ghosting handling
Without diodes, multiple keys pressed on the same receive line cause short‑circuits. Adding a diode in series with each switch prevents this ghosting.
Voltage fluctuation handling
To eliminate voltage bounce when a scan line goes low, pull‑down resistors are added to each receive line. Adding capacitors in parallel with the switches smooths contact bounce.
Final circuit diagram
Case design
Left half:
Right half:
Side view:
Keycap design
Program
#include "Keyboard.h"
#include "HID.h"
#define scanPin_len 8
int scanPin[] = {4,5,6,7,0,1,2,3}; // scan pins (default low, set high one by one)
int scanPos = 0; // current scan position
#define btnPinA_len 6
#define btnPinD_len 4
int btnPinA[] = {5,4,3,2,1,0}; // analog pins for buttons
int btnPinD[] = {8,9,10,11}; // digital pins for buttons
#define btn_len 10
byte btn[scanPin_len][btn_len]; // button states
byte btnTmp[btn_len]; // temporary button states
#define KEY_FN KEY_RIGHT_SHIFT // FN key
// 8*10 key mapping matrix
uint8_t keyMap[scanPin_len][btn_len] = {
{'y','n','7','8',KEY_F6,'h','m','u','j',' '},
{'o','.', '0','9',KEY_F7,'l',',','i','k',KEY_FN},
{'p','/','-','\x1B',KEY_F8,';','\x1B','[','\'','\x1B'},
{KEY_F10,KEY_DELETE,'=','\x08',KEY_F9,KEY_F11,KEY_RETURN,']','\\',KEY_RIGHT_ARROW},
{KEY_ESC,KEY_LEFT_GUI,'`',KEY_LEFT_CTRL,KEY_TAB,'a','q','z',KEY_CAPS_LOCK,KEY_LEFT_SHIFT},
{KEY_F1,KEY_LEFT_ALT,'1',KEY_F2,'2','s','w','x','d','c'},
{KEY_F3,' ', '4',KEY_F4,'3','e','r','b','f','v'},
{KEY_F5,'6','5',0,0,0,'t','g',0,0}
};
void setup() {
Keyboard.begin();
Keyboard.releaseAll();
for(int i=0;i<scanPin_len;i++) pinMode(scanPin[i], OUTPUT);
for(int i=0;i<btnPinD_len;i++) pinMode(btnPinD[i], INPUT);
for(int i=0;i<scanPin_len;i++) {
for(int j=0;j<btn_len;j++) btn[i][j] = 0;
}
for(int j=0;j<btn_len;j++) btnTmp[j] = 0;
}
void loop() {
for(int i=0;i<scanPin_len;i++) {
if(i == scanPos) digitalWrite(scanPin[i], HIGH);
else digitalWrite(scanPin[i], LOW);
}
delay(5);
readBtn();
for(int i=0;i<btn_len;i++) {
if(btn[scanPos][i] != btnTmp[i]) {
btn[scanPos][i] = btnTmp[i];
if(btnTmp[i] == 1) Keyboard.press(keyMap[scanPos][i]);
else Keyboard.release(keyMap[scanPos][i]);
}
}
scanPos = (scanPos + 1) % scanPin_len;
}
void readBtn() {
int index = 0;
for(int i=0;i<btnPinA_len;i++) {
int val = analogRead(btnPinA[i]);
btnTmp[index++] = (val > 600) ? 1 : 0;
}
for(int i=0;i<btnPinD_len;i++) {
btnTmp[index++] = digitalRead(btnPinD[i]);
}
}Production photos
First‑generation keycaps (3D‑printed):
Printing issues (glass plate broke, returned to matte paper):
Heated head fell off during printing but the printer’s safety stopped a fire.
Fly‑wired prototype without diodes (no PCB):
Testing with an Arduino Uno before Leonardo arrived:
Assembled case (left and right halves):
Final keycaps (white, printed on higher‑quality material):
Exposed circuit board gives a geeky look:
Conclusion
The finished split mechanical keyboard provides a satisfying typing experience, eliminates wrist strain, and can be fully customized in layout and firmware. The total cost was roughly 160 CNY, considerably cheaper than commercial ergonomic keyboards, and the project demonstrates that building a personal mechanical keyboard is well within reach for hobbyists.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent TDS Service
TDS Service offers client and web front‑end developers and operators an intelligent low‑code platform, cross‑platform development framework, universal release platform, runtime container engine, monitoring and analysis platform, and a security‑privacy compliance suite.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
