You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A fully custom, 3D-printed, Bluetooth split mechanical keyboard built without a PCB.
2
+
3
+
A fully custom, 3D-printed, Bluetooth split mechanical keyboard built without a PCB, running Rust firmware on nRF52840 microcontrollers.
3
4
4
5
:::info
5
6
@@ -12,87 +13,183 @@ A fully custom, 3D-printed, Bluetooth split mechanical keyboard built without a
12
13
13
14
## Description
14
15
15
-
This project is a custom-built, wireless split mechanical keyboard based on the ergonomic Corne physical layout. To maximize hardware complexity and avoid prebuilt kits, the keyboard uses no PCB; instead, the switch matrix is entirely handwired. The core of the project is writing a custom embedded Rust firmware from scratch using the embassy-rs framework to handle matrix scanning, key debouncing, and Bluetooth Low Energy (BLE) communication.
16
+
This project is a custom-built, wireless split mechanical keyboard based on the ergonomic **Corne** layout (4 rows × 6 columns per half, plus a 3-key thumb row on each side). There is no PCB: every switch is handwired into a diode matrix and mounted in a 3D-printed plate!
17
+
18
+
Each half uses a **SuperMini nRF52840** (Adafruit nRF52 Bootloader, UF2 drag-and-drop flashing). The firmware is written in **Rust** and built on the **RMK 0.8** framework with a **dual-binary BLE split** architecture: the left half acts as the **central** (USB/BLE to the PC, full keymap), and the right half acts as the **peripheral** (scans its matrix and sends coordinates over Bluetooth). Configuration lives in `keyboard.toml` (pins, matrix map, three layers).
19
+
20
+
The original plan was to implement matrix scanning, debouncing, and a BLE HID keyboard stack manually with **embassy-rs** and Nordic’s softdevice bindings. After roughly a week of attempts where the nRF52840 radio/HID path never came together reliably, the project moved to **RMK**, which still uses Embassy and modern Rust async patterns under the hood but provides a proven split-BLE and keymap pipeline.
16
21
17
22
## Motivation
18
23
19
-
I chose this project to gain deep, hands-on experience with both bare-metal electronics and modern embedded software. On the hardware side, handwiring the diode matrix forces a complete understanding of how electrical grids operate. On the software side, writing the firmware in Rust provides the opportunity to learn embassy-rs for asynchronous embedded programming and to understand how to implement a BLE HID (Human Interface Device) stack from the ground up. Also I want to some watch movies in the summer on my tv and I need a keyboard to type on the internet so that the search is way faster.
24
+
I chose this project to get real experience on both sides of a custom keyboard: bare-metal wiring and modern embedded Rust.
25
+
26
+
On the **hardware** side, handwiring forces you to understand rows, columns, diodes, ghosting, and how few GPIO pins can serve many keys. On the **software** side, I wanted to learn how a keyboard actually becomes a BLE HID device not just flash someone else’s binary.
27
+
28
+
A practical goal too: a compact wireless keyboard for the TV/PC so searching and typing away from the desk is faster than a remote.
29
+
30
+
## Architecture
20
31
21
-
##Architecture
32
+
### Hardware
22
33
23
-
- The system architecture consists of a custom electrical matrix and an asynchronous Rust software stack.
34
+
-**Layout**: Split Corne — 21 keys per half (18 in the main 3×6 grid + 3 thumb keys).
35
+
-**Matrix**: 4×6 per half; **1N4148 diodes** (column → row) to prevent ghosting.
36
+
-**Controllers**: Two **SuperMini nRF52840** boards (nice!nano v2–class clones).
37
+
-**Power**: **3.7 V LiPo** per half with a slide power switch.
38
+
-**Structure**: 3D-printed switch plate and case; **24 AWG** solid wire for row/column buses.
24
39
25
-
- The Hardware Matrix: 42 mechanical switches are handwired into a matrix of Rows and Columns. Diodes are soldered to each switch to prevent electrical "ghosting" when multiple keys are pressed.
40
+
### Firmware (final)
26
41
27
-
- The Microcontroller: An NRF52840 development board acts as the central brain, connected to the matrix rows and columns via its GPIO pins. It is powered by a 3.7V LiPo battery.
42
+
| Role | Binary | UF2 output | Responsibility |
43
+
|------|--------|------------|----------------|
44
+
| Left half |`central`|`NEW_LEFT.uf2`| BLE central, matrix scan (cols 0–5), USB/BLE HID to host, full keymap |
45
+
| Right half |`peripheral`|`peripheral.uf2`| BLE peripheral, matrix scan (cols 6–11 via `col_offset = 6`), sends `(row, col)` to central |
28
46
29
-
- The Rust Firmware: The software utilizes embassy-rs for async execution. A polling task continuously scans the GPIO matrix to detect state changes.
47
+
**Pin map (both halves, local wiring)** — inner column toward the split, outer toward the pinky:
30
48
31
-
- Bluetooth Communication: Using the nrf-softdevice crate, the microcontroller advertises itself as a BLE Keyboard and transmits the processed keystroke data over the air to a host computer.
49
+
| Row | MCU pin |
50
+
|-----|---------|
51
+
| Row 1 (top) | P0_31 |
52
+
| Row 2 | P0_29 |
53
+
| Row 3 | P0_02 |
54
+
| Row 4 (thumb) | P1_15 |
55
+
56
+
| Col | MCU pin |
57
+
|-----|---------|
58
+
| Col 1 (inner) | P1_06 |
59
+
| Col 2 | P1_04 |
60
+
| Col 3 | P0_11 |
61
+
| Col 4 | P1_00 |
62
+
| Col 5 | P0_24 |
63
+
| Col 6 (outer) | P0_22 |
64
+
65
+
**Keymap (3 layers)** — defined in `keyboard.toml`:
66
+
67
+
-**Layer 0**: QWERTY Corne base; left thumb `MO(1)` (symbols), right thumb `MO(2)` (nav/F-keys/BT).
68
+
-**Layer 1**: Numbers and symbols (`\``~``!` … `[]``{}`, etc.).
69
+
-**Layer 2**: F-keys, arrows, paging, BLE profile/clear (`User1` / `User2` / `User5`), and modifier combos (`WM(...)`) for shortcuts.
70
+
71
+
**Build & flash**
72
+
73
+
```text
74
+
cargo build --release --bin central → NEW_LEFT.uf2 (left only)
Family ID for UF2: `0xada52840` (Adafruit nRF52). Flash memory layout reserves the bootloader region (`FLASH` from `0x1000`).
80
+
81
+
### Software path (what actually happened)
82
+
83
+
1.**Week ~1 (failed path)**: Custom `embassy-nrf` + softdevice/BLE HID experiments—matrix ideas worked in isolation, but getting a stable BLE keyboard device on the nRF52840 did not, despite many rebuild/flash cycles.
84
+
2.**Final path**: **RMK 0.8.2** with `rmk_central` / `rmk_peripheral` macros, `keyboard.toml`, and **Vial-compatible** storage. Minimal application code (`central.rs`, `peripheral.rs`); complexity moved into configuration and build tooling.
32
85
33
86
## Log
34
87
35
-
<!--write your progress here every week-->
88
+
<!--Progress log (~3-day intervals). Hardware and bring-up took the majority of the project time.-->
36
89
37
-
### Week 5 - 11 May
90
+
### 5–8 May
38
91
39
-
Finalized component research and ordered all necessary hardware (microcontrollers, switches, batteries, keycaps) -> Waiting for their delievery.
92
+
Finalized the Corne layout choice and BOM. Ordered SuperMini nRF52840 boards, Gateron Milky Yellow switches, 1N4148 diodes, LiPo cells, wire, keycaps, and power switches. Started the 3D model/plate while waiting on shipping.
40
93
41
-
### Week 12 - 18 May
94
+
### 9–11 May
42
95
43
-
So far, I've 3D printed the baseplate for the switches to snap into. I went with a hand-wired matrix setup, including diodes for reliable input. I've also soldered a power switch and a battery, which I tested with a multimeter. I connected the columns and one of the rows to the microcontroller just to check the continuity. This is all for the first half of the build; I plan to wrap up the other half later this week.
96
+
Parts still in transit. Read nRF52840 docs, Embassy book chapters, and split-keyboard matrix guides. Sketched row/column routing on paper so the handwire would not be guesswork later.
44
97
45
-
### Week 19 - 25 May
98
+
### 12–15 May
46
99
47
-
## Hardware
100
+
Printed the switch plate and case pieces. Began the **left half** handwire: diode orientation (column → row), column buses along the inner edge, first row soldered. Multimeter continuity checks after every few keys—caught one reversed diode early.
48
101
49
-
The hardware relies on a 3D-printed chassis to hold the switches, eliminating the need for a PCB. The logic is handled by NRF52840 chips due to their low power consumption and excellent Bluetooth capabilities.
102
+
### 16–18 May
50
103
51
-

104
+
Finished the left matrix and installed the LiPo + slide switch. Smoke test: power only, then tweezers on row/column pins. Connected one row and one column to the SuperMini to confirm a single intersection could be detected. **Right half not started yet**—wanted one working grid first.
52
105
53
-

106
+
### 19–21 May
54
107
55
-
### Schematics
108
+
Completed **right half** wiring using the same pinout as the left (per-half local row/col numbering). Set up the Rust toolchain (Windows + WSL), `thumbv7em-none-eabihf` target, `flip-link`, and `cargo-binutils`. First flashes used a single-binary approach; behavior was inconsistent on the split.
56
109
57
-

110
+
### 22–24 May
58
111
59
-
### Bill of Materials
112
+
**Hardest software stretch:** tried to implement the keyboard **without RMK**—Embassy executor, GPIO matrix scan, debouncing, and BLE HID using Nordic stack crates (`nrf-softdevice` / related paths). Spent about **a week** here: device would compile and flash, but BLE advertising/HID reports never behaved like a real keyboard on the host. nRF52840 + softdevice + HID from scratch was a wall; pivoted to **RMK** as a maintained framework that already solves split BLE and keymaps.
60
113
61
-
<!-- Fill out this table with all the hardware components that you might need.
114
+
### 25–27 May
62
115
63
-
The format is
64
-
```
65
-
| [Device](link://to/device) | This is used ... | [price](link://to/store) |
116
+
Regenerated the project around **RMK 0.8** dual binaries (`central` / `peripheral`). Configured `keyboard.toml` (matrix pins, `matrix_map`, BLE addresses). **Left controller** refused to take new firmware for a while (old image kept running); later fixed with proper UF2 flash and `clear_storage`. Right half flashed as `peripheral.uf2` and paired over BLE. Hit Windows path length limits during builds moved/cloned the repo to a shorter path.
66
117
67
-
```
118
+
### 28 May
68
119
69
-
-->
120
+
Left matrix still typed garbage: all keys repeating one character, then a scrambled QWERTY-like grid. Root causes: (1) **stale keymap in flash** (RMK Vial storage overriding `keyboard.toml`), (2) **wiring** not matching the assumed column order. Rewired to the documented pin table; `clear_storage = true` once to prove `keyboard.toml` was live (`Esc` / `Q` test grid). Confirmed **Corne Split** BLE name and stable per-key output.
70
121
71
-
| Device | Usage | Price |
72
-
|--------|--------|-------|
73
-
|[SuperMini NRF52840](https://shorturl.at/LceaZ)| The microcontroller |[45 RON](https://shorturl.at/Ukoo9)|
74
-
|[Liter Energy 120mAh 3.7V LiPo (401230)](https://shorturl.at/YeYWx)| Power supply |[100 RON](https://shorturl.at/ZWF5S)|
75
-
|[Gateron Milky Yellow Pro](https://shorturl.at/b2qIj)| Mechanical linear keyboard switches |[125 RON](https://www.gateron.co/products/gateron-ks-3-milky-pro-switch-set)|
76
-
|[1N4148 High-speed Diodes](https://www.vishay.com/docs/81857/1n4148.pdf)| Prevents "ghosting" in the keyboard matrix |[16 RON](https://rb.gy/56u5x7)|
77
-
|[24AWG Solid Core Copper Wire](https://www.optimusdigital.ro/en/kits/11950-plusivo-silicone-wire-kit-24awg-6-colors-9m-each-0721248989734.html)| Routing the rows and columns |[24 RON](https://www.aliexpress.com/item/1005010758626132.html?spm=a2g0o.order_list.order_list_main.17.b1b61802RMNohO)|
|[SS12F15VG4 4MM Slide Switches](https://leeselectronic.com/qc/product/31191-slide-switch-chassis-mount-on-on-spdt-1a-125vac.html)| Physical power toggle for the batteries |[36 RON](https://www.aliexpress.com/item/1005010262474497.html?spm=a2g0o.order_list.order_list_main.25.531a1802SqISZa)|
122
+
### 29 May
123
+
124
+
Removed `clear_storage` (kept `clear_layout` while iterating). Implemented full **3-layer Corne keymap** (base, symbols `MO(1)`, nav/F-keys/BT `MO(2)`). Rebuilt `NEW_LEFT.uf2`; left + right halves work together over BLE. **Project functionally complete.**
125
+
126
+
## Hardware
127
+
128
+
The hardware relies on a 3D-printed chassis to hold the switches, eliminating the need for a PCB. Logic is handled by nRF52840 SuperMini boards for low power and BLE.
129
+
130
+

80
131
132
+

81
133
134
+

135
+
136
+
### Schematics
137
+
138
+

139
+
140
+
### Bill of Materials
141
+
142
+
| Device | Usage | Price |
143
+
|--------|--------|-------|
144
+
|[SuperMini NRF52840](https://shorturl.at/LceaZ)| Microcontroller (one per half) |[45 RON](https://shorturl.at/Ukoo9)|
145
+
|[Liter Energy 120mAh 3.7V LiPo (401230)](https://shorturl.at/YeYWx)| Power supply (per half) |[100 RON](https://shorturl.at/ZWF5S)|
146
+
|[Gateron Milky Yellow Pro](https://shorturl.at/b2qIj)| Linear switches |[125 RON](https://www.gateron.co/products/gateron-ks-3-milky-pro-switch-set)|
147
+
|[1N4148 diodes](https://www.vishay.com/docs/81857/1n4148.pdf)| Anti-ghosting in the matrix |[16 RON](https://rb.gy/56u5x7)|
|[SS12F15 slide switches](https://leeselectronic.com/qc/product/31191-slide-switch-chassis-mount-on-on-spdt-1a-125vac.html)| Battery power switch |[36 RON](https://www.aliexpress.com/item/1005010262474497.html)|
82
151
83
152
## Software
84
153
154
+
### Libraries used (final firmware)
155
+
85
156
| Library | Description | Usage |
86
157
|---------|-------------|-------|
87
-
|[embassy-rs](https://github.com/embassy-rs/embassy)| Async embedded framework for Rust | The core framework used to run asynchronous tasks (like matrix scanning and Bluetooth handling) without blocking the CPU |
88
-
|[embassy-nrf](https://crates.io/crates/embassy-nrf)| Hardware Abstraction Layer (HAL) | Used to safely interact with the NRF52840's physical GPIO pins and timers |
89
-
|[nrf-softdevice](https://github.com/embassy-rs/nrf-softdevice)| Rust bindings for Nordic's Bluetooth stack | Used to configure the BLE radio and implement the HID protocol to talk to the PC |
90
-
|[defmt](https://github.com/knurling-rs/defmt)| Highly efficient logging framework | Used for debugging the matrix scanning logic over a USB connection |
**Complete.** Both halves type over BLE with a three-layer Corne keymap. Future tweaks are `keyboard.toml` edits + reflash `NEW_LEFT.uf2` on the central only; the peripheral only needs a reflash if matrix pins or BLE pairing metadata change.
0 commit comments