Skip to content

Commit 37522c4

Browse files
nmunnichRasmusKoitcaksoylar
committed
docs: added a page on dongles.
Co-authored-by: rasmuskoit <rasmuskoit@gmail.com> Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
1 parent 40925d4 commit 37522c4

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
---
2+
title: Keyboard Dongle
3+
sidebar_label: Keyboard Dongle
4+
---
5+
6+
import Tabs from "@theme/Tabs";
7+
import TabItem from "@theme/TabItem";
8+
9+
A bluetooth dongle can be added to any keyboard running ZMK. The result is a [split keyboard](../../features/split-keyboards.md) with the dongle as ["central"](../../features/split-keyboards.md#central-and-peripheral-roles). There are a number of advantages to adding a dongle, but also some disadvantages:
10+
11+
Benefits:
12+
13+
- For split keyboards, having the dongle for the "central" role results in [improved battery life](/power-profiler) for the former central part, as it is now a peripheral.
14+
- It is easier to connect to the host device since dongles are typically connected via USB.
15+
16+
Disadvantages:
17+
18+
- An extra [board](../../faq.md#what-is-a-board) is needed (any BLE-capable board will work).
19+
- The keyboard becomes unusable without the dongle.
20+
21+
Depending on how the dongle is used, there are some additional [latency considerations](../../features/split-keyboards.md#latency-considerations) to keep in mind.
22+
The addition of the dongle adds an extra "hop" for the former central, increasing its latency to that of a peripheral. The other parts are unchanged latency-wise. There is also a commonly occurring case where the peripherals benefit.
23+
Assuming the dongle is connected to USB and the former central would have been connected via bluetooth to the host if the dongle wasn't present:
24+
25+
- The former central will have its latency increased by about 1ms from the extra USB "hop"
26+
- The other parts will have their average latency _decreased_ by 6.5ms from the replacement of a BLE "hop" with a "USB" hop.
27+
28+
As a result, for this common use case the average latency of the keyboard decreases.
29+
30+
## Adding a Dongle
31+
32+
The approach taken to adding a dongle is _generally_ always the same[^1].
33+
Whether your keyboard consists of a board and a shield or is just a board is irrelevant.
34+
35+
To add a dongle, you will create a simplified form of a [new shield](new-shield.mdx).
36+
The approach described below assumes the dongle will not have any keys assigned to itself, so it will not work for that scenario. If you want to understand the details of how it all works better, please read through the [new shield guide](new-shield.mdx).
37+
38+
As there are a very large number of possible devices that could be used as a dongle, you will be defining your dongle as a personal shield intended for your exclusive use.
39+
40+
Prior to adding a dongle to your keyboard, please test its functionality without a dongle. The below guide will assume that your keyboard is named `my_keyboard`, replace accordingly.
41+
42+
### Dongle Folder
43+
44+
First, make sure that your `zmk-config` matches the folder structure found in the [unified ZMK config template](https://github.com/zmkfirmware/unified-zmk-config-template) (extra files and folders are fine, but none should be missing).
45+
46+
Next, navigate to the `zmk-config/boards/shields` directory. Create a subdirectory called `my_keyboard`, if one doesn't already exist. Unless otherwise specified, the below files should all be placed in said folder.
47+
48+
### Kconfig Files
49+
50+
#### Kconfig.shield
51+
52+
Make a file called `Kconfig.shield`, if one does not exist already. Add the following lines to it, replacing `SHIELD_MY_KEYBOARD_DONGLE` and `my_keyboard_dongle` according to your keyboard:
53+
54+
```kconfig title="Kconfig.shield"
55+
# No whitespace after the comma or in the keyboard name!
56+
config SHIELD_MY_KEYBOARD_DONGLE
57+
def_bool $(shields_list_contains,my_keyboard_dongle)
58+
```
59+
60+
#### Kconfig.defconfig
61+
62+
Make a file called `Kconfig.defconfig`, if one does not exist already. Add the following lines to it where `SHIELD_MY_KEYBOARD_DONGLE` should match the previous section:
63+
64+
```kconfig title="Kconfig.defconfig"
65+
if SHIELD_MY_KEYBOARD_DONGLE
66+
67+
# Max 16 characters in keyboard name
68+
config ZMK_KEYBOARD_NAME
69+
default "My Board"
70+
71+
config ZMK_SPLIT_ROLE_CENTRAL
72+
default y
73+
74+
config ZMK_SPLIT
75+
default y
76+
77+
# Set this to the number of peripherals your dongle will have.
78+
# For a unibody, this would be 1. If you have left and right halves, set it to two, etc.
79+
config ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS
80+
default 1
81+
82+
# Set this to ZMK_SPLIT_BLE_CENTRAL_PERIPHERALS + your desired number of BT profiles (default is 5)
83+
config BT_MAX_CONN
84+
default 6
85+
86+
# Set this to the same number as BT_MAX_CONN
87+
config BT_MAX_PAIRED
88+
default 6
89+
90+
endif
91+
```
92+
93+
### Dongle Overlay File
94+
95+
Make a file called `my_keyboard_dongle.overlay` (renamed as appropriate).
96+
This file will include your keyboard's matrix transform and physical layout, allowing it to map key press events from the peripherals to behaviors.
97+
98+
Create it with the following lines:
99+
100+
```dts title="my_keyboard_dongle.overlay"
101+
#include <dt-bindings/zmk/matrix_transform.h>
102+
103+
/ {
104+
chosen {
105+
zmk,kscan = &mock_kscan;
106+
};
107+
108+
mock_kscan: mock_kscan_0 {
109+
compatible = "zmk,kscan-mock";
110+
columns = <0>;
111+
rows = <0>;
112+
events = <0>;
113+
};
114+
};
115+
```
116+
117+
You will now need to find and copy your keyboard's matrix transform into the `my_keyboard_dongle.overlay` file.
118+
119+
#### Matrix transform
120+
121+
Navigate to the directory defining your keyboard (in-tree keyboards found [here](https://github.com/zmkfirmware/zmk/tree/main/app/boards)) and look through the [devicetree files](../../config/index.md) for nodes with `compatible = "zmk,matrix-transform";`.
122+
This should look something like this:
123+
124+
```dts
125+
default_transform: keymap_transform_0 {
126+
compatible = "zmk,matrix-transform";
127+
columns = <14>;
128+
rows = <5>;
129+
130+
map = <
131+
// Lots of RC(r,c) macros
132+
>;
133+
};
134+
```
135+
136+
Copy this node into your `my_keyboard_dongle.overlay` file. Take care to put it under the root node (the `/ { \* Stuff *\ };` node).
137+
If there are multiple matrix transformations, copy the one that matches your keyboard's layout.
138+
139+
Make a note of the label that the transform has, it will be used later. In the example the label is `default_transform`.
140+
141+
#### Physical layout
142+
143+
A full physical layout is necessary to allow your dongle to be used with [ZMK Studio](../../features/studio.md).
144+
If your keyboard is not Studio-ready or you have no interest in using ZMK Studio with your dongle, then this section is simplified significantly.
145+
146+
<Tabs
147+
groupId="studio-ready"
148+
defaultValue="studio"
149+
values={[
150+
{ label: "Dongle with Studio", value: "studio" },
151+
{ label: "Dongle without Studio", value: "nostudio" }
152+
]}
153+
>
154+
<TabItem value="studio">
155+
156+
You will need to find your keyboard's physical layout, much like finding the matrix transform.
157+
Depending on your keyboard, the physical layout could be found in a variety of locations. Look for a node with `compatible = "zmk,physical-layout";`.
158+
There are three commonly found possibilities:
159+
160+
- The physical layout is found in a file called `my_keyboard-layouts.dtsi`
161+
- The physical layout is found in the same file as the matrix transform and chosen node
162+
- The physical layout is imported from ZMK's shared layouts - `#include <layouts/<stuff>.dtsi>` can be found at the top of one of the devicetree files.
163+
164+
<Tabs
165+
groupId="studio-ready"
166+
defaultValue="studio"
167+
values={[
168+
{ label: "Layouts File", value: "layouts" },
169+
{ label: "Base File", value: "base" },
170+
{ label: "Imported", value: "imported" }
171+
]}
172+
>
173+
<TabItem value="layouts">
174+
Copy the file into your dongle's folder. Then import the file, assign the matrix transform to it, and select it in the `chosen` node:
175+
176+
```dts
177+
#import "my_keyboard-layouts.dtsi"
178+
179+
&physical_layout0 {
180+
transform = <&default_transform>;
181+
};
182+
183+
/ {
184+
chosen {
185+
zmk,kscan = &mock_kscan;
186+
zmk,physical-layout = &physical_layout0;
187+
};
188+
};
189+
```
190+
191+
Make sure that the labels `physical_layout0` and `default_transform` match those of the physical layout node and the matrix transform respectively.
192+
193+
If there are multiple physical layouts in the file, you will need to copy over all of the remaining matrix transformations and assign them similarly.
194+
195+
</TabItem>
196+
<TabItem value="base">
197+
Copy the physical layout node into your `my_keyboard_dongle.overlay` file. Make sure the matrix transform is assigned to it, and select it in the `chosen` node.
198+
199+
```dts
200+
/ {
201+
chosen {
202+
zmk,kscan = &mock_kscan;
203+
zmk,physical-layout = &physical_layout0;
204+
};
205+
206+
physical_layout0: physical_layout_0 {
207+
compatible = "zmk,physical-layout";
208+
display-name = "Default Layout";
209+
transform = <&default_transform>;
210+
keys = //Long list of keys
211+
};
212+
};
213+
```
214+
215+
Make sure that the labels `physical_layout0` and `default_transform` match those of the physical layout node and the matrix transform respectively.
216+
217+
If there are multiple physical layouts, you need only copy the one that applies to your keyboard.
218+
219+
</TabItem>
220+
<TabItem value="imported">
221+
Include your desired layout at the top of the file. Then assign your matrix transform to it, taking care to match up the node labels correctly.
222+
223+
```dts
224+
#include <layouts/<stuff>.dtsi>
225+
226+
&physical_layout0 {
227+
transform = <&default_transform>;
228+
};
229+
```
230+
231+
Your node label will never be `physical_layout0`, make sure to adjust it accordingly.
232+
233+
Finally, select your physical layout in the chosen node.
234+
235+
```dts
236+
/ {
237+
chosen {
238+
zmk,kscan = &kscan0;
239+
zmk,physical-layout = &physical_layout0;
240+
};
241+
};
242+
```
243+
244+
</TabItem>
245+
</Tabs>
246+
247+
</TabItem>
248+
<TabItem value="nostudio">
249+
Add a barebones [physical layout](physical-layouts.md) without the `keys` property to your `my_keyboard_dongle.overlay` file.
250+
251+
```dts
252+
/ {
253+
physical_layout0: physical_layout_0 {
254+
compatible = "zmk,physical-layout";
255+
display-name = "Default Layout";
256+
transform = <&default_transform>;
257+
};
258+
};
259+
```
260+
261+
Make sure that the `transform` property uses the same label as your matrix transform. Then select the physical layout in the same `chosen` node as previously written:
262+
263+
```dts
264+
/ {
265+
chosen {
266+
zmk,kscan = &mock_kscan;
267+
zmk,physical-layout = &physical_layout0;
268+
};
269+
};
270+
```
271+
272+
</TabItem>
273+
</Tabs>
274+
275+
## Building the Firmware
276+
277+
:::warning
278+
Before flashing your new firmware, you need to flash `settings_reset` [firmware](../../troubleshooting/connection-issues.mdx#acquiring-a-reset-uf2) on all devices to ensure they can pair to each other.
279+
:::
280+
281+
Add the appropriate lines to your `build.yml` file to build the firmware for your dongle. Also add some CMake arguments using `cmake-args` to the existing parts of your keyboard, turning them into peripherals for your dongle.
282+
283+
```yaml
284+
include:
285+
# -----------------------------------------
286+
# Your other keyboard parts here
287+
# -----------------------------------------
288+
# Change the board appropriately, you can use any board
289+
- board: nice_nano_v2
290+
shield: my_keyboard_dongle
291+
- board: nice_nano_v2
292+
shield: settings_reset
293+
# Add these cmake-args to the peripherals you wish to use with the dongle
294+
- board: nice_nano_v2
295+
shield: my_keyboard
296+
cmake-args: -DZMK_SPLIT=y -DZMK_SPLIT_ROLE_CENTRAL=n
297+
```
298+
299+
If you ever want to "undongle" your keyboard, simply remove these CMake arguments and flash the resulting firmware (after a `settings_reset`).
300+
301+
[^1]: If you have a custom dongle that uses an onboard MCU, then you will need to take a slightly different approach.

docs/sidebars.js

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ module.exports = {
134134
"development/hardware-integration/physical-layouts",
135135
"development/hardware-integration/hardware-metadata-files",
136136
"development/hardware-integration/pinctrl",
137+
"development/hardware-integration/dongle",
137138
"development/hardware-integration/shift-registers",
138139
"development/hardware-integration/encoders",
139140
"development/hardware-integration/soft-off-setup",

0 commit comments

Comments
 (0)