Carey 4 years ago
parent
commit
99ad9cbdef
  1. 4
      .devcontainer/.bashrc
  2. 5
      .devcontainer/devcontainer.json
  3. 3
      .github/workflows/build.yml
  4. 1
      .gitignore
  5. 2
      .vscode/settings.json
  6. 2
      README.md
  7. 2
      app/CMakeLists.txt
  8. 3
      app/boards/arm/bluemicro840/Kconfig.defconfig
  9. 12
      app/boards/arm/bluemicro840/bluemicro840_v1.dts
  10. 3
      app/boards/arm/nice_nano/Kconfig.defconfig
  11. 12
      app/boards/arm/nice_nano/nice_nano.dts
  12. 3
      app/boards/arm/nrfmicro/Kconfig.defconfig
  13. 12
      app/boards/arm/nrfmicro/nrfmicro_13.dts
  14. 14
      app/boards/shields/Kconfig.defconfig
  15. 5
      app/boards/shields/Kconfig.shield
  16. 9
      app/boards/shields/boardsource3x4/Kconfig.defconfig
  17. 6
      app/boards/shields/boardsource3x4/Kconfig.shield
  18. 0
      app/boards/shields/boardsource3x4/boardsource3x4.conf
  19. 49
      app/boards/shields/boardsource3x4/boardsource3x4.keymap
  20. 32
      app/boards/shields/boardsource3x4/boardsource3x4.overlay
  21. 23
      app/boards/shields/cradio/Kconfig.defconfig
  22. 8
      app/boards/shields/cradio/Kconfig.shield
  23. 50
      app/boards/shields/cradio/cradio.dtsi
  24. 43
      app/boards/shields/cradio/cradio.keymap
  25. 5
      app/boards/shields/cradio/cradio_left.conf
  26. 7
      app/boards/shields/cradio/cradio_left.overlay
  27. 5
      app/boards/shields/cradio/cradio_right.conf
  28. 11
      app/boards/shields/cradio/cradio_right.overlay
  29. 11
      app/boards/shields/lily58/lily58.keymap
  30. 13
      app/boards/shields/nibble/Kconfig.defconfig
  31. 5
      app/boards/shields/nibble/Kconfig.shield
  32. 0
      app/boards/shields/nibble/nibble.conf
  33. 37
      app/boards/shields/nibble/nibble.keymap
  34. 49
      app/boards/shields/nibble/nibble.overlay
  35. 0
      app/boards/shields/qaz/qaz.conf
  36. 9
      app/boards/shields/reviung41/Kconfig.defconfig
  37. 5
      app/boards/shields/reviung41/Kconfig.shield
  38. 28
      app/boards/shields/reviung41/boards/nice_nano.overlay
  39. 3
      app/boards/shields/reviung41/reviung41.conf
  40. 72
      app/boards/shields/reviung41/reviung41.keymap
  41. 52
      app/boards/shields/reviung41/reviung41.overlay
  42. 9
      app/boards/shields/tg4x/Kconfig.defconfig
  43. 5
      app/boards/shields/tg4x/Kconfig.shield
  44. 58
      app/boards/shields/tg4x/tg4x.keymap
  45. 56
      app/boards/shields/tg4x/tg4x.overlay
  46. 2
      app/drivers/zephyr/CMakeLists.txt
  47. 6
      app/drivers/zephyr/Kconfig
  48. 215
      app/drivers/zephyr/battery_voltage_divider.c
  49. 14
      app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml
  50. 22
      app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-demux.yaml
  51. 254
      app/drivers/zephyr/kscan_gpio_demux.c
  52. 25
      app/drivers/zephyr/kscan_gpio_matrix.c
  53. 3
      app/dts/behaviors.dtsi
  54. 9
      app/dts/behaviors/ext_power.dtsi
  55. 10
      app/dts/bindings/behaviors/zmk,behavior-ext-power.yaml
  56. 2
      app/include/dt-bindings/zmk/bt.h
  57. 13
      app/include/dt-bindings/zmk/ext_power.h
  58. 2
      app/include/zmk/ble/profile.h
  59. 2
      app/include/zmk/events/ble-active-profile-changed.h
  60. 2
      app/src/behaviors/behavior_bt.c
  61. 57
      app/src/behaviors/behavior_ext_power.c
  62. 2
      app/src/behaviors/behavior_key_press.c
  63. 146
      app/src/ble.c
  64. 2
      app/src/events/ble_active_profile_changed.c
  65. 12
      app/src/hog.c
  66. 2
      app/src/kscan_mock.c
  67. 95
      app/src/rgb_underglow.c
  68. 8
      app/src/settings.c
  69. 2
      docs/.eslintignore
  70. 29
      docs/.eslintrc.js
  71. 3
      docs/.prettierignore
  72. 3
      docs/.prettierrc.js
  73. 5
      docs/README.md
  74. 1
      docs/blog/2020-05-24-wip.md
  75. 15
      docs/docs/behavior/hold-tap.md
  76. 2
      docs/docs/behavior/key-press.md
  77. 2
      docs/docs/behavior/layers.md
  78. 1
      docs/docs/behavior/mod-tap.md
  79. 64
      docs/docs/behavior/power.md
  80. 3
      docs/docs/bond-reset.md
  81. 28
      docs/docs/customization.md
  82. 9
      docs/docs/dev-build.md
  83. 198
      docs/docs/dev-guide-new-shield.md
  84. 2
      docs/docs/dev-guide-usb-logging.md
  85. 13
      docs/docs/dev-setup.md
  86. 9
      docs/docs/dev-tests.md
  87. 47
      docs/docs/faq.md
  88. 16
      docs/docs/feature/underglow.md
  89. 4
      docs/docs/hardware.md
  90. 63
      docs/docs/intro.md
  91. 61
      docs/docs/troubleshooting.md
  92. 10
      docs/docs/user-setup.md
  93. 3
      docs/docusaurus.config.js
  94. 24610
      docs/package-lock.json
  95. 15
      docs/package.json
  96. 7
      docs/sidebars.js
  97. 20
      docs/src/pages/index.js
  98. 6
      docs/static/setup.ps1
  99. 57
      docs/static/setup.sh

4
.devcontainer/.bashrc

@ -1,6 +1,8 @@
export LS_OPTIONS='-F --color=auto' export LS_OPTIONS='-F --color=auto'
eval "`dircolors`"
alias ls='ls $LS_OPTIONS' alias ls='ls $LS_OPTIONS'
if [ "${CODESPACES}" = "true" ]; then
export WORKSPACE_DIR="$HOME/workspace/zmk"
fi
if [ -f "$WORKSPACE_DIR/zephyr/zephyr-env.sh" ]; then if [ -f "$WORKSPACE_DIR/zephyr/zephyr-env.sh" ]; then
source "$WORKSPACE_DIR/zephyr/zephyr-env.sh" source "$WORKSPACE_DIR/zephyr/zephyr-env.sh"
fi fi

5
.devcontainer/devcontainer.json

@ -3,6 +3,9 @@
"dockerFile": "Dockerfile", "dockerFile": "Dockerfile",
"extensions": ["ms-vscode.cpptools"], "extensions": ["ms-vscode.cpptools"],
"runArgs": ["--security-opt", "label=disable"], "runArgs": ["--security-opt", "label=disable"],
"containerEnv": {"WORKSPACE_DIR": "${containerWorkspaceFolder}"} "containerEnv": {"WORKSPACE_DIR": "${containerWorkspaceFolder}"},
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
} }

3
.github/workflows/build.yml

@ -10,6 +10,7 @@ jobs:
matrix: matrix:
board: [nice_nano, bluemicro840_v1, nrfmicro_13] board: [nice_nano, bluemicro840_v1, nrfmicro_13]
shield: shield:
- boardsource3x4
- corne_left - corne_left
- corne_right - corne_right
- kyria_left - kyria_left
@ -18,11 +19,13 @@ jobs:
- lily58_right - lily58_right
- iris_left - iris_left
- iris_right - iris_right
- reviung41
- romac - romac
- romac_plus - romac_plus
- settings_reset - settings_reset
- quefrency_left - quefrency_left
- quefrency_right - quefrency_right
- nibble
include: include:
- board: proton_c - board: proton_c
shield: clueboard_california shield: clueboard_california

1
.gitignore vendored

@ -3,5 +3,6 @@
/modules /modules
/tools /tools
/zephyr /zephyr
/zmk-config
/build /build
*.DS_Store *.DS_Store

2
.vscode/settings.json vendored

@ -3,4 +3,4 @@
"*.overlay": "dts", "*.overlay": "dts",
"*.keymap": "dts" "*.keymap": "dts"
} }
} }

2
README.md

@ -5,7 +5,7 @@
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
This project is a complete work in progress, with only basic things functioning. The goal is to explore a new MK firmware This project is a complete work in progress, with only basic things functioning. The goal is to explore a new MK firmware
with a less restritive license and better BLE support, built on top of the [Zephyr™ Project](https://www.zephyrproject.org/) with a less restrictive license and better BLE support, built on top of the [Zephyr™ Project](https://www.zephyrproject.org/)
Check out the website to learn more: https://zmkfirmware.dev/ Check out the website to learn more: https://zmkfirmware.dev/

2
app/CMakeLists.txt

@ -46,6 +46,7 @@ if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources(app PRIVATE src/behaviors/behavior_none.c)
target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
target_sources(app PRIVATE src/keymap.c) target_sources(app PRIVATE src/keymap.c)
endif() endif()
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/behaviors/behavior_rgb_underglow.c)
@ -61,6 +62,7 @@ target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c)
target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c) target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c)
target_sources(app PRIVATE src/endpoints.c) target_sources(app PRIVATE src/endpoints.c)
target_sources(app PRIVATE src/hid_listener.c) target_sources(app PRIVATE src/hid_listener.c)
target_sources_ifdef(CONFIG_SETTINGS app PRIVATE src/settings.c)
target_sources(app PRIVATE src/main.c) target_sources(app PRIVATE src/main.c)
zephyr_cc_option(-Wfatal-errors) zephyr_cc_option(-Wfatal-errors)

3
app/boards/arm/bluemicro840/Kconfig.defconfig

@ -27,4 +27,7 @@ config ZMK_BLE
config ZMK_USB config ZMK_USB
default y default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_BLUEMICRO840_V1 endif # BOARD_BLUEMICRO840_V1

12
app/boards/arm/bluemicro840/bluemicro840_v1.dts

@ -29,6 +29,18 @@
}; };
}; };
vbatt {
compatible = "zmk,battery-voltage-divider";
label = "VOLTAGE_DIVIDER";
io-channels = <&adc 7>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 806000)>;
};
};
&adc {
status = "okay";
}; };
&gpio0 { &gpio0 {

3
app/boards/arm/nice_nano/Kconfig.defconfig

@ -25,4 +25,7 @@ config ZMK_BLE
config ZMK_USB config ZMK_USB
default y default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NICE_NANO endif # BOARD_NICE_NANO

12
app/boards/arm/nice_nano/nice_nano.dts

@ -34,6 +34,18 @@
label = "EXT_POWER"; label = "EXT_POWER";
control-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; control-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
}; };
vbatt {
compatible = "zmk,battery-voltage-divider";
label = "VOLTAGE_DIVIDER";
io-channels = <&adc 2>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 806000)>;
};
};
&adc {
status = "okay";
}; };
&gpiote { &gpiote {

3
app/boards/arm/nrfmicro/Kconfig.defconfig

@ -35,6 +35,9 @@ if BOARD_NRFMICRO_13
config BOARD_NRFMICRO_CHARGER config BOARD_NRFMICRO_CHARGER
default y default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # BOARD_NRFMICRO_13 endif # BOARD_NRFMICRO_13
endif # BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13 endif # BOARD_NRFMICRO_11 || BOARD_NRFMICRO_11_FLIPPED || BOARD_NRFMICRO_13

12
app/boards/arm/nrfmicro/nrfmicro_13.dts

@ -31,6 +31,18 @@
label = "EXT_POWER"; label = "EXT_POWER";
control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; control-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
}; };
vbatt {
compatible = "zmk,battery-voltage-divider";
label = "VOLTAGE_DIVIDER";
io-channels = <&adc 2>;
output-ohms = <2000000>;
full-ohms = <(2000000 + 820000)>;
};
};
&adc {
status = "okay";
}; };
&gpio0 { &gpio0 {

14
app/boards/shields/Kconfig.defconfig

@ -0,0 +1,14 @@
config ZMK_KEYBOARD_NAME
default "cradios"
# Unable to use interrupts as the same pin number is used
# across A & B controllers, and STM32F303CCT6 can't enable
# interrutps for multiple controllers for the same "line"
# for the external interrupts.
config ZMK_KSCAN_GPIO_POLLING
default y

5
app/boards/shields/Kconfig.shield

@ -0,0 +1,5 @@
# Copyright (c) 2020 Pete Johanson
# SPDX-License-Identifier: MIT
config SHIELD_CRADIOS
def_bool $(shields_list_contains,cradios)

9
app/boards/shields/boardsource3x4/Kconfig.defconfig

@ -0,0 +1,9 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_BOARDSOURCE3X4
config ZMK_KEYBOARD_NAME
default "Boardsource 3x4"
endif

6
app/boards/shields/boardsource3x4/Kconfig.shield

@ -0,0 +1,6 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_BOARDSOURCE3X4
def_bool $(shields_list_contains,boardsource3x4)

0
app/boards/shields/boardsource3x4/boardsource3x4.conf

49
app/boards/shields/boardsource3x4/boardsource3x4.keymap

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&tog 1 &cp M_PREV &cp M_PLAY &cp M_NEXT
&mo 2 &cp M_VOLD &kp UARW &cp M_VOLU
&mo 3 &kp LARW &kp DARW &kp RARW
>;
};
num_layer {
bindings = <
&trans &kp NUM_7 &kp NUM_8 &kp NUM_9
&trans &kp NUM_4 &kp NUM_5 &kp NUM_6
&lt 3 NUM_0 &kp NUM_1 &kp NUM_2 &kp NUM_3
>;
};
lower_layer {
bindings = <
&bt BT_CLR &none &reset &bootloader
&trans &bt BT_SEL 3 &bt BT_SEL 4 &none
&none &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2
>;
};
raise_layer {
bindings = <
&kp 0x68 &kp 0x69 &kp 0x6A &kp 0x6B
&kp 0x6C &kp 0x6D &kp 0x6E &kp 0x6F
&trans &kp 0x70 &kp 0x71 &kp 0x72
>;
};
};
};

32
app/boards/shields/boardsource3x4/boardsource3x4.overlay

@ -0,0 +1,32 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&pro_micro_a 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&pro_micro_d 10 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 16 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 14 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 15 GPIO_ACTIVE_HIGH>
;
};
};

23
app/boards/shields/cradio/Kconfig.defconfig

@ -0,0 +1,23 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_CRADIO_LEFT
config ZMK_KEYBOARD_NAME
default "cradio left"
endif
if SHIELD_CRADIO_RIGHT
config ZMK_KEYBOARD_NAME
default "cradio right"
endif
if SHIELD_CRADIO_RIGHT || SHIELD_CRADIO_LEFT
config ZMK_KSCAN_DIRECT_POLLING
default y
endif

8
app/boards/shields/cradio/Kconfig.shield

@ -0,0 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_CRADIO_LEFT
def_bool $(shields_list_contains,cradio_left)
config SHIELD_CRADIO_RIGHT
def_bool $(shields_list_contains,cradio_right)

50
app/boards/shields/cradio/cradio.dtsi

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
//zmk,matrix_transform = &default_transform;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <34>;
rows = <1>;
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,21) RC(0,20) RC(0,19) RC(0,18) RC(0,17)
RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,26) RC(0,25) RC(0,24) RC(0,23) RC(0,22)
RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14) RC(0,31) RC(0,30) RC(0,29) RC(0,28) RC(0,27)
RC(0,15) RC(0,16) RC(0,33) RC(0,32)
>;
kscan0: kscan {
compatible = "zmk,kscan-gpio-direct";
label = "KSCAN";
input-gpios
= <&pro_micro_d 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_a 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_a 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_a 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_a 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 1 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 3 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_a 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 6 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 9 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
;
};
};
};

43
app/boards/shields/cradio/cradio.keymap

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P
&kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SCLN
&kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &kp FSLH
&mo 1 &kp LCTL &kp SPC &mo 2
>;
};
upper_layer {
bindings = <
&kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0
&bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &kp H &kp J &kp K &kp L &kp SCLN
&kp LSFT &trans &trans &trans &trans &trans &trans &trans &trans &trans
&mo 1 &kp LCTL &kp SPC &mo 2
>;
};
lower_layer {
bindings = <
&kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN
&trans &trans &trans &trans &trans &kp MINUS &kp EQL &kp LBKT &kp RBKT &kp PIPE
&trans &trans &trans &trans &trans &trans &trans &trans &kp BSLH &kp TILD
&mo 1 &kp LCTL &kp SPC &mo 2
>;
};
};
};

5
app/boards/shields/cradio/cradio_left.conf

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
CONFIG_ZMK_SPLIT=y
CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL=y

7
app/boards/shields/cradio/cradio_left.overlay

@ -0,0 +1,7 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "cradio.dtsi"

5
app/boards/shields/cradio/cradio_right.conf

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
CONFIG_ZMK_SPLIT=y
CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL=y

11
app/boards/shields/cradio/cradio_right.overlay

@ -0,0 +1,11 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "cradio.dtsi"
&default_transform {
col-offset = <17>;
};

11
app/boards/shields/lily58/lily58.keymap

@ -7,6 +7,7 @@
#include <behaviors.dtsi> #include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h> #include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h> #include <dt-bindings/zmk/bt.h>
#include <dt-bindings/zmk/ext_power.h>
/ { / {
keymap { keymap {
@ -38,11 +39,11 @@
// | | | | | | | | | | | _ | + | { | } | "|" | // | | | | | | | | | | | _ | + | { | } | "|" |
// | | | | | | | | | | // | | | | | | | | | |
bindings = < bindings = <
&bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans &bt BT_CLR &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &trans &trans &trans &trans &trans &trans
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
&kp GRAV &kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN &kp TILD &kp GRAV &kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp KMLT &kp LPRN &kp RPRN &kp TILD
&trans &trans &trans &trans &trans &trans &trans &trans &trans &kp MINUS &kp KPLS &kp LCUR &kp RCUR &kp PIPE &trans &ext_power EP_ON &ext_power EP_OFF &ext_power EP_TOG &trans &trans &trans &trans &trans &kp MINUS &kp KPLS &kp LCUR &kp RCUR &kp PIPE
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
>; >;
sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>; sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>;

13
app/boards/shields/nibble/Kconfig.defconfig

@ -0,0 +1,13 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_NIBBLE
config ZMK_KEYBOARD_NAME
default "NIBBLE"
config ZMK_USB
default y
endif

5
app/boards/shields/nibble/Kconfig.shield

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_NIBBLE
def_bool $(shields_list_contains,nibble)

0
app/boards/shields/nibble/nibble.conf

37
app/boards/shields/nibble/nibble.keymap

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#define DEFAULT 0
#define FUNC 1
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&kp ESC &kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0 &kp MINUS &kp EQL &kp BKSP &kp HOME
&cp M_VOLU &kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp LBKT &kp RBKT &kp BSLH &kp DEL
&cp M_VOLD &kp CLCK &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SCLN &kp QUOT &kp RET &kp PGUP
&trans &kp LSFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &kp FSLH &kp RSFT &kp UARW &kp PGDN
&trans &kp LCTL &kp LGUI &kp LALT &kp SPC &mo FUNC &kp RALT &kp RCTL &kp LARW &kp DARW &kp RARW
>;
};
func {
bindings = <
&kp TILD &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &trans &kp END
&bt BT_CLR &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &bootloader
&trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&bt BT_PRV &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans &trans
&bt BT_NXT &trans &trans &trans &trans &trans &trans &trans &cp M_PREV &cp M_PLAY &cp M_NEXT
>;
};
};
};

49
app/boards/shields/nibble/nibble.overlay

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-demux";
label = "KSCAN";
polling-interval-msec = <25>;
input-gpios
= <&pro_micro_d 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 10 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
, <&pro_micro_d 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>
;
output-gpios
= <&pro_micro_a 3 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 2 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 1 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 0 GPIO_ACTIVE_HIGH>
;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <16>;
rows = <5>;
//TODO: Add a keymap graphic here
map = <
RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13) RC(0,14) RC(0,15)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13) RC(1,14) RC(1,15)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,12) RC(2,14) RC(2,15)
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,12) RC(3,14) RC(3,15)
RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,6) RC(4,9) RC(4,10) RC(4,11) RC(4,12) RC(4,14) RC(4,15)
>;
};
};

0
app/boards/shields/qaz/qaz.conf

9
app/boards/shields/reviung41/Kconfig.defconfig

@ -0,0 +1,9 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_REVIUNG41
config ZMK_KEYBOARD_NAME
default "Reviung41"
endif

5
app/boards/shields/reviung41/Kconfig.shield

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_REVIUNG41
def_bool $(shields_list_contains,reviung41)

28
app/boards/shields/reviung41/boards/nice_nano.overlay

@ -0,0 +1,28 @@
&spi1 {
compatible = "nordic,nrf-spi";
status = "okay";
mosi-pin = <6>;
// Unused pins, needed for SPI definition, but not used by the ws2812 driver itself.
sck-pin = <5>;
miso-pin = <7>;
led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";
label = "WS2812";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <4000000>;
/* WS2812 */
chain-length = <11>; /* arbitrary; change at will */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
};
};
/ {
chosen {
zmk,underglow = &led_strip;
};
};

3
app/boards/shields/reviung41/reviung41.conf

@ -0,0 +1,3 @@
# Uncomment the following lines to enable RGB underglow
# CONFIG_ZMK_RGB_UNDERGLOW=y
# CONFIG_WS2812_STRIP=y

72
app/boards/shields/reviung41/reviung41.keymap

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#include <dt-bindings/zmk/rgb.h>
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
// -------------------------------------------------------------------------------------
// | TAB | Q | W | E | R | T | | Y | U | I | O | P | BKSP |
// | CTRL | A | S | D | F | G | | H | J | K | L | ; | ' |
// | SHFT | Z | X | C | V | B | | N | M | , | . | / | SHFT(RET) |
// | ALT | LWR | SPC | RSE | ALT |
bindings = <
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BKSP
&kp LCTL &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SCLN &kp QUOT
&kp LSFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &kp FSLH &mt RSFT RET
&kp LALT &mo 1 &kp SPC &mo 2 &kp RALT
>;
};
lower_layer {
// ----------------------------------------------------------------------------------
// | | ! | @ | # | $ | % | | ^ | & | * | ( | ) | DEL |
// | | _ | + | { | } | "|" | | LFT | DWN | UP | RGT | ` | ~ |
// | | ESC | GUI | ALT | CAPS| " | | HOME| END | PGUP| PGDN| PRSC| SHFT(RET) |
// | | | RET | ADJ | |
bindings = <
&trans &kp BANG &kp ATSN &kp HASH &kp CURU &kp PRCT &kp CRRT &kp AMPS &kp NUM_8 &kp LPRN &kp RPRN &kp DEL
&trans &kp MINUS &kp KPLS &kp LCUR &kp RCUR &kp PIPE &kp LARW &kp DARW &kp UARW &kp RARW &kp GRAV &kp GRAV
&trans &kp ESC &kp LGUI &kp LALT &kp CLCK &kp QUOT &kp HOME &kp END &kp PGUP &kp PGDN &kp PRSC &mt RSFT RET
&trans &trans &kp RET &mo 3 &trans
>;
};
raise_layer {
// -----------------------------------------------------------------------------------------
// | | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | DEL |
// | | - | = | [ | ] | \ | | F1 | F2 | F3 | F4 | F5 | F6 |
// | | ESC | GUI | ALT | CAPS| " | | F7 | F8 | F9 | F10 | F11 | F12 |
// | | ADJ | BKSP | | |
bindings = <
&trans &kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0 &kp DEL
&trans &kp MINUS &kp EQL &kp LBKT &kp RBKT &kp BSLH &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6
&trans &kp ESC &kp LGUI &kp RALT &kp CLCK &kp QUOT &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12
&trans &mo 3 &kp BKSP &trans &trans
>;
};
adjust_layer {
// -----------------------------------------------------------------------------------------
// | RGB BRI+ | RGB SAT+ | RGB HUE+ | RGB ANI+ | | RGB TOG | | BT1 | BT2 | BT3 | BT4 | BT5 | BT CLR |
// | RGB BRI- | RGB SAT- | RGB HUE- | RGB ANI+ | | | | | | | | | |
// | | | | | | | | RESET | | | | | |
// | | | | | |
bindings = <
&rgb_ug RGB_BRI &rgb_ug RGB_SAI &rgb_ug RGB_HUI &rgb_ug RGB_EFF &none &rgb_ug RGB_TOG &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3 &bt BT_SEL 4 &bt BT_CLR
&rgb_ug RGB_BRD &rgb_ug RGB_SAD &rgb_ug RGB_HUD &rgb_ug RGB_EFR &none &none &none &none &none &none &none &none
&none &none &none &none &none &none &reset &none &none &none &none &none
&trans &trans &tog 3 &trans &trans
>;
};
};
};

52
app/boards/shields/reviung41/reviung41.overlay

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <6>;
rows = <7>;
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(5,5)
RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4)
>;
};
kscan0: kscan_0 {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
col-gpios
= <&pro_micro_d 4 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 5 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 6 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 7 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 8 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 9 GPIO_ACTIVE_HIGH>
;
row-gpios
= <&pro_micro_a 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 1 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
};
};

9
app/boards/shields/tg4x/Kconfig.defconfig

@ -0,0 +1,9 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
if SHIELD_TG4X
config ZMK_KEYBOARD_NAME
default "TG4X"
endif

5
app/boards/shields/tg4x/Kconfig.shield

@ -0,0 +1,5 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
config SHIELD_TG4X
def_bool $(shields_list_contains,tg4x)

58
app/boards/shields/tg4x/tg4x.keymap

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#define DEFAULT 0
#define LOWER 1
#define RAISE 2
/ {
behaviors {
hm: homerow_mods {
compatible = "zmk,behavior-hold-tap";
label = "homerow mods";
#binding-cells = <2>;
tapping_term_ms = <225>;
flavor = "tap-preferred";
bindings = <&kp>, <&kp>;
};
};
};
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
bindings = <
&kp ESC &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BKSP
&kp TAB &hm LGUI A &hm LALT S &hm LCTL D &hm LSFT F &kp G &kp H &hm RSFT J &hm RCTL K &hm RALT L &hm RGUI SCLN &kp RET
&kp LSFT &kp Z &kp X &kp C &kp V &kp B &kp N &kp M &kp CMMA &kp DOT &kp FSLH &kp QUOT
&kp LCTL &kp LALT &kp LGUI &lt 1 BKSP &lt 2 SPC &kp LARW &kp DARW &kp UARW &kp RARW
>;
};
lower {
bindings = <
&kp GRAV &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp PRSC
&kp DEL &trans &kp VOLU &trans &trans &trans &trans &kp LARW &kp DARW &kp UARW &kp RARW &trans
&trans &trans &kp VOLD &trans &trans &trans &trans &trans &trans &bt BT_PRV &bt BT_NXT &bt BT_CLR
&bootloader &reset &trans &trans &trans &trans &bt BT_SEL 0 &bt BT_SEL 1 &bt BT_SEL 2 &bt BT_SEL 3
>;
};
raise {
bindings = <
&kp GRAV &kp NUM_1 &kp NUM_2 &kp NUM_3 &kp NUM_4 &kp NUM_5 &kp NUM_6 &kp NUM_7 &kp NUM_8 &kp NUM_9 &kp NUM_0 &kp PRSC
&kp DEL &kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp MINUS &kp EQL &kp LBKT &kp RBKT &kp BSLH
&trans &kp F7 &kp F8 &kp F9 &kp F10 &kp F11 &kp F12 &kp TILD &kp HOME &kp PGUP &kp PGDN &kp END
&trans &trans &trans &trans &trans &trans &kp M_NEXT &kp M_VOLD &kp M_VOLU &kp M_PLAY
>;
};
};
};

56
app/boards/shields/tg4x/tg4x.overlay

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 The ZMK Contrbutors
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <7>;
rows = <8>;
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,5)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(6,0) RC(6,1) RC(6,2) RC(6,3) RC(6,4)
RC(3,0) RC(3,1) RC(3,2) RC(3,4) RC(3,5) RC(7,1) RC(7,2) RC(7,3) RC(7,4)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
col-gpios
= <&pro_micro_d 1 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 14 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 15 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 0 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 1 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 2 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 3 GPIO_ACTIVE_HIGH>
;
row-gpios
= <&pro_micro_a 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_a 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
};
};

2
app/drivers/zephyr/CMakeLists.txt

@ -5,8 +5,10 @@ if(CONFIG_ZMK_KSCAN_GPIO_DRIVER)
zephyr_library_sources( zephyr_library_sources(
kscan_gpio_matrix.c kscan_gpio_matrix.c
kscan_gpio_direct.c kscan_gpio_direct.c
kscan_gpio_demux.c
) )
zephyr_library_sources_ifdef(CONFIG_EC11 ec11.c) zephyr_library_sources_ifdef(CONFIG_EC11 ec11.c)
zephyr_library_sources_ifdef(CONFIG_EC11_TRIGGER ec11_trigger.c) zephyr_library_sources_ifdef(CONFIG_EC11_TRIGGER ec11_trigger.c)
zephyr_library_sources_ifdef(CONFIG_ZMK_BATTERY_VOLTAGE_DIVIDER battery_voltage_divider.c)
endif() endif()

6
app/drivers/zephyr/Kconfig

@ -21,6 +21,12 @@ config ZMK_KSCAN_INIT_PRIORITY
help help
Keyboard scan device driver initialization priority. Keyboard scan device driver initialization priority.
config ZMK_BATTERY_VOLTAGE_DIVIDER
bool "ZMK battery voltage divider"
select ADC
help
Enable ZMK battery voltage divider driver for battery monitoring.
menuconfig EC11 menuconfig EC11
bool "EC11 Incremental Encoder Sensor" bool "EC11 Incremental Encoder Sensor"
depends on GPIO depends on GPIO

215
app/drivers/zephyr/battery_voltage_divider.c

@ -0,0 +1,215 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_battery_voltage_divider
#include <device.h>
#include <drivers/gpio.h>
#include <drivers/adc.h>
#include <drivers/sensor.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct io_channel_config {
const char *label;
uint8_t channel;
};
struct gpio_channel_config {
const char *label;
uint8_t pin;
uint8_t flags;
};
struct bvd_config {
struct io_channel_config io_channel;
struct gpio_channel_config power_gpios;
uint32_t output_ohm;
uint32_t full_ohm;
};
struct bvd_data {
struct device *adc;
struct device *gpio;
struct adc_channel_cfg acc;
struct adc_sequence as;
uint16_t adc_raw;
uint16_t voltage;
uint8_t state_of_charge;
};
static uint8_t lithium_ion_mv_to_pct(int16_t bat_mv) {
// Simple linear approximation of a battery based off adafruit's discharge graph:
// https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages
if (bat_mv >= 4200) {
return 100;
} else if (bat_mv <= 3450) {
return 0;
}
return bat_mv * 2 / 15 - 459;
}
static int bvd_sample_fetch(struct device *dev, enum sensor_channel chan) {
struct bvd_data *drv_data = dev->driver_data;
const struct bvd_config *drv_cfg = dev->config_info;
struct adc_sequence *as = &drv_data->as;
// Make sure selected channel is supported
if (chan != SENSOR_CHAN_GAUGE_VOLTAGE && chan != SENSOR_CHAN_GAUGE_STATE_OF_CHARGE) {
return -ENOTSUP;
}
int rc = 0;
// Enable power GPIO if present
if (drv_data->gpio) {
rc = gpio_pin_set(drv_data->gpio, drv_cfg->power_gpios.pin, 1);
if (rc != 0) {
LOG_DBG("Failed to enable ADC power GPIO: %d", rc);
return rc;
}
}
// Read ADC
rc = adc_read(drv_data->adc, as);
as->calibrate = false;
if (rc == 0) {
int32_t val = drv_data->adc_raw;
adc_raw_to_millivolts(adc_ref_internal(drv_data->adc), drv_data->acc.gain, as->resolution,
&val);
uint16_t millivolts = val * (uint64_t)drv_cfg->full_ohm / drv_cfg->output_ohm;
LOG_DBG("ADC raw %d ~ %d mV => %d mV\n", drv_data->adc_raw, val, millivolts);
uint8_t percent = lithium_ion_mv_to_pct(millivolts);
LOG_DBG("Percent: %d", percent);
drv_data->voltage = millivolts;
drv_data->state_of_charge = percent;
} else {
LOG_DBG("Failed to read ADC: %d", rc);
}
// Disable power GPIO if present
if (drv_data->gpio) {
int rc2 = gpio_pin_set(drv_data->gpio, drv_cfg->power_gpios.pin, 0);
if (rc2 != 0) {
LOG_DBG("Failed to disable ADC power GPIO: %d", rc2);
return rc2;
}
}
return rc;
}
static int bvd_channel_get(struct device *dev, enum sensor_channel chan, struct sensor_value *val) {
struct bvd_data *drv_data = dev->driver_data;
switch (chan) {
case SENSOR_CHAN_GAUGE_VOLTAGE:
val->val1 = drv_data->voltage / 1000;
val->val2 = (drv_data->voltage % 1000) * 1000U;
break;
case SENSOR_CHAN_GAUGE_STATE_OF_CHARGE:
val->val1 = drv_data->state_of_charge;
val->val2 = 0;
break;
default:
return -ENOTSUP;
}
return 0;
}
static const struct sensor_driver_api bvd_api = {
.sample_fetch = bvd_sample_fetch,
.channel_get = bvd_channel_get,
};
static int bvd_init(struct device *dev) {
struct bvd_data *drv_data = dev->driver_data;
const struct bvd_config *drv_cfg = dev->config_info;
drv_data->adc = device_get_binding(drv_cfg->io_channel.label);
if (drv_data->adc == NULL) {
LOG_ERR("ADC %s failed to retrieve", drv_cfg->io_channel.label);
return -ENODEV;
}
int rc = 0;
if (drv_cfg->power_gpios.label) {
drv_data->gpio = device_get_binding(drv_cfg->power_gpios.label);
if (drv_data->gpio == NULL) {
LOG_ERR("Failed to get GPIO %s", drv_cfg->power_gpios.label);
return -ENODEV;
}
rc = gpio_pin_configure(drv_data->gpio, drv_cfg->power_gpios.pin,
GPIO_OUTPUT_INACTIVE | drv_cfg->power_gpios.flags);
if (rc != 0) {
LOG_ERR("Failed to control feed %s.%u: %d", drv_cfg->power_gpios.label,
drv_cfg->power_gpios.pin, rc);
return rc;
}
}
drv_data->as = (struct adc_sequence){
.channels = BIT(0),
.buffer = &drv_data->adc_raw,
.buffer_size = sizeof(drv_data->adc_raw),
.oversampling = 4,
.calibrate = true,
};
#ifdef CONFIG_ADC_NRFX_SAADC
drv_data->acc = (struct adc_channel_cfg){
.gain = ADC_GAIN_1_5,
.reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
.input_positive = SAADC_CH_PSELP_PSELP_AnalogInput0 + drv_cfg->io_channel.channel,
};
drv_data->as.resolution = 12;
#else
#error Unsupported ADC
#endif
rc = adc_channel_setup(drv_data->adc, &drv_data->acc);
LOG_DBG("AIN%u setup returned %d", drv_cfg->io_channel.channel, rc);
return rc;
}
static struct bvd_data bvd_data;
static const struct bvd_config bvd_cfg = {
.io_channel =
{
DT_INST_IO_CHANNELS_LABEL(0),
DT_INST_IO_CHANNELS_INPUT(0),
},
#if DT_INST_NODE_HAS_PROP(0, power_gpios)
.power_gpios =
{
DT_INST_GPIO_LABEL(0, power_gpios),
DT_INST_PIN(0, power_gpios),
DT_INST_FLAGS(0, power_gpios),
},
#endif
.output_ohm = DT_INST_PROP(0, output_ohms),
.full_ohm = DT_INST_PROP(0, full_ohms),
};
DEVICE_AND_API_INIT(bvd_dev, DT_INST_LABEL(0), &bvd_init, &bvd_data, &bvd_cfg, POST_KERNEL,
CONFIG_SENSOR_INIT_PRIORITY, &bvd_api);

14
app/drivers/zephyr/dts/bindings/zmk,battery-voltage-divider.yaml

@ -0,0 +1,14 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Battery SoC monitoring using voltage divider
compatible: "zmk,battery-voltage-divider"
include: voltage-divider.yaml
properties:
label:
required: true
type: string

22
app/drivers/zephyr/dts/bindings/zmk,kscan-gpio-demux.yaml

@ -0,0 +1,22 @@
# Copyright (c) 2020, The ZMK Contributors
# SPDX-License-Identifier: MIT
description: GPIO keyboard demux controller
compatible: "zmk,kscan-gpio-demux"
include: kscan.yaml
properties:
input-gpios:
type: phandle-array
required: true
output-gpios:
type: phandle-array
required: true
debounce-period:
type: int
default: 5
polling-interval-msec:
type: int
default: 25

254
app/drivers/zephyr/kscan_gpio_demux.c

@ -0,0 +1,254 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_kscan_gpio_demux
#include <device.h>
#include <drivers/kscan.h>
#include <drivers/gpio.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct kscan_gpio_item_config {
char *label;
gpio_pin_t pin;
gpio_flags_t flags;
};
// Helper macro
#define PWR_TWO(x) (1 << (x))
// Define GPIO cfg
#define _KSCAN_GPIO_ITEM_CFG_INIT(n, prop, idx) \
{ \
.label = DT_INST_GPIO_LABEL_BY_IDX(n, prop, idx), \
.pin = DT_INST_GPIO_PIN_BY_IDX(n, prop, idx), \
.flags = DT_INST_GPIO_FLAGS_BY_IDX(n, prop, idx), \
},
// Define row and col cfg
#define _KSCAN_GPIO_INPUT_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, input_gpios, idx)
#define _KSCAN_GPIO_OUTPUT_CFG_INIT(idx, n) _KSCAN_GPIO_ITEM_CFG_INIT(n, output_gpios, idx)
// Check debounce config
#define CHECK_DEBOUNCE_CFG(n, a, b) COND_CODE_0(DT_INST_PROP(n, debounce_period), a, b)
// Define the row and column lengths
#define INST_MATRIX_INPUTS(n) DT_INST_PROP_LEN(n, input_gpios)
#define INST_DEMUX_GPIOS(n) DT_INST_PROP_LEN(n, output_gpios)
#define INST_MATRIX_OUTPUTS(n) PWR_TWO(INST_DEMUX_GPIOS(n))
#define POLL_INTERVAL(n) DT_INST_PROP(n, polling_interval_msec)
#define GPIO_INST_INIT(n) \
struct kscan_gpio_irq_callback_##n { \
struct CHECK_DEBOUNCE_CFG(n, (k_work), (k_delayed_work)) * work; \
struct gpio_callback callback; \
struct device *dev; \
}; \
\
struct kscan_gpio_config_##n { \
struct kscan_gpio_item_config rows[INST_MATRIX_INPUTS(n)]; \
struct kscan_gpio_item_config cols[INST_DEMUX_GPIOS(n)]; \
}; \
\
struct kscan_gpio_data_##n { \
kscan_callback_t callback; \
struct k_timer poll_timer; \
struct CHECK_DEBOUNCE_CFG(n, (k_work), (k_delayed_work)) work; \
bool matrix_state[INST_MATRIX_INPUTS(n)][INST_MATRIX_OUTPUTS(n)]; \
struct device *rows[INST_MATRIX_INPUTS(n)]; \
struct device *cols[INST_MATRIX_OUTPUTS(n)]; \
struct device *dev; \
}; \
/* IO/GPIO SETUP */ \
/* gpio_input_devices are PHYSICAL IO devices */ \
static struct device **kscan_gpio_input_devices_##n(struct device *dev) { \
struct kscan_gpio_data_##n *data = dev->driver_data; \
return data->rows; \
} \
\
static const struct kscan_gpio_item_config *kscan_gpio_input_configs_##n(struct device *dev) { \
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
return cfg->rows; \
} \
\
/* gpio_output_devices are PHYSICAL IO devices */ \
static struct device **kscan_gpio_output_devices_##n(struct device *dev) { \
struct kscan_gpio_data_##n *data = dev->driver_data; \
return data->cols; \
} \
\
static const struct kscan_gpio_item_config *kscan_gpio_output_configs_##n( \
struct device *dev) { \
const struct kscan_gpio_config_##n *cfg = dev->config_info; \
/* If row2col, rows = outputs & cols = inputs */ \
return cfg->cols; \
} \
/* POLLING SETUP */ \
static void kscan_gpio_timer_handler(struct k_timer *timer) { \
struct kscan_gpio_data_##n *data = \
CONTAINER_OF(timer, struct kscan_gpio_data_##n, poll_timer); \
k_work_submit(&data->work.work); \
} \
\
/* Read the state of the input GPIOs */ \
/* This is the core matrix_scan func */ \
static int kscan_gpio_read_##n(struct device *dev) { \
bool submit_follow_up_read = false; \
struct kscan_gpio_data_##n *data = dev->driver_data; \
static bool read_state[INST_MATRIX_INPUTS(n)][INST_MATRIX_OUTPUTS(n)]; \
for (int o = 0; o < INST_MATRIX_OUTPUTS(n); o++) { \
/* Iterate over bits and set GPIOs accordingly */ \
for (u8_t bit = 0; bit < INST_DEMUX_GPIOS(n); bit++) { \
u8_t state = (o & (0b1 << bit)) >> bit; \
struct device *out_dev = kscan_gpio_output_devices_##n(dev)[bit]; \
const struct kscan_gpio_item_config *out_cfg = \
&kscan_gpio_output_configs_##n(dev)[bit]; \
gpio_pin_set(out_dev, out_cfg->pin, state); \
} \
\
for (int i = 0; i < INST_MATRIX_INPUTS(n); i++) { \
/* Get the input device (port) */ \
struct device *in_dev = kscan_gpio_input_devices_##n(dev)[i]; \
/* Get the input device config (pin) */ \
const struct kscan_gpio_item_config *in_cfg = \
&kscan_gpio_input_configs_##n(dev)[i]; \
read_state[i][o] = gpio_pin_get(in_dev, in_cfg->pin) > 0; \
} \
} \
for (int r = 0; r < INST_MATRIX_INPUTS(n); r++) { \
for (int c = 0; c < INST_MATRIX_OUTPUTS(n); c++) { \
bool pressed = read_state[r][c]; \
submit_follow_up_read = (submit_follow_up_read || pressed); \
if (pressed != data->matrix_state[r][c]) { \
LOG_DBG("Sending event at %d,%d state %s", r, c, (pressed ? "on" : "off")); \
data->matrix_state[r][c] = pressed; \
data->callback(dev, r, c, pressed); \
} \
} \
} \
if (submit_follow_up_read) { \
CHECK_DEBOUNCE_CFG(n, ({ k_work_submit(&data->work); }), ({ \
k_delayed_work_cancel(&data->work); \
k_delayed_work_submit(&data->work, K_MSEC(5)); \
})) \
} \
return 0; \
} \
\
static void kscan_gpio_work_handler_##n(struct k_work *work) { \
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
kscan_gpio_read_##n(data->dev); \
} \
\
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
.rows = {[INST_MATRIX_INPUTS(n) - 1] = NULL}, .cols = {[INST_DEMUX_GPIOS(n) - 1] = NULL}}; \
\
/* KSCAN API configure function */ \
static int kscan_gpio_configure_##n(struct device *dev, kscan_callback_t callback) { \
LOG_DBG("KSCAN API configure"); \
struct kscan_gpio_data_##n *data = dev->driver_data; \
if (!callback) { \
return -EINVAL; \
} \
data->callback = callback; \
LOG_DBG("Configured GPIO %d", n); \
return 0; \
}; \
\
/* KSCAN API enable function */ \
static int kscan_gpio_enable_##n(struct device *dev) { \
LOG_DBG("KSCAN API enable"); \
struct kscan_gpio_data_##n *data = dev->driver_data; \
/* TODO: we might want a follow up to hook into the sleep state hooks in Zephyr, */ \
/* and disable this timer when we enter a sleep state */ \
k_timer_start(&data->poll_timer, K_MSEC(POLL_INTERVAL(n)), K_MSEC(POLL_INTERVAL(n))); \
return 0; \
}; \
\
/* KSCAN API disable function */ \
static int kscan_gpio_disable_##n(struct device *dev) { \
LOG_DBG("KSCAN API disable"); \
struct kscan_gpio_data_##n *data = dev->driver_data; \
k_timer_stop(&data->poll_timer); \
return 0; \
}; \
\
/* GPIO init function*/ \
static int kscan_gpio_init_##n(struct device *dev) { \
LOG_DBG("KSCAN GPIO init"); \
struct kscan_gpio_data_##n *data = dev->driver_data; \
int err; \
/* configure input devices*/ \
struct device **input_devices = kscan_gpio_input_devices_##n(dev); \
for (int i = 0; i < INST_MATRIX_INPUTS(n); i++) { \
const struct kscan_gpio_item_config *in_cfg = &kscan_gpio_input_configs_##n(dev)[i]; \
input_devices[i] = device_get_binding(in_cfg->label); \
if (!input_devices[i]) { \
LOG_ERR("Unable to find input GPIO device"); \
return -EINVAL; \
} \
err = gpio_pin_configure(input_devices[i], in_cfg->pin, GPIO_INPUT | in_cfg->flags); \
if (err) { \
LOG_ERR("Unable to configure pin %d on %s for input", in_cfg->pin, in_cfg->label); \
return err; \
} else { \
LOG_DBG("Configured pin %d on %s for input", in_cfg->pin, in_cfg->label); \
} \
if (err) { \
LOG_ERR("Error adding the callback to the column device"); \
return err; \
} \
} \
/* configure output devices*/ \
struct device **output_devices = kscan_gpio_output_devices_##n(dev); \
for (int o = 0; o < INST_DEMUX_GPIOS(n); o++) { \
const struct kscan_gpio_item_config *out_cfg = &kscan_gpio_output_configs_##n(dev)[o]; \
output_devices[o] = device_get_binding(out_cfg->label); \
if (!output_devices[o]) { \
LOG_ERR("Unable to find output GPIO device"); \
return -EINVAL; \
} \
err = gpio_pin_configure(output_devices[o], out_cfg->pin, \
GPIO_OUTPUT_ACTIVE | out_cfg->flags); \
if (err) { \
LOG_ERR("Unable to configure pin %d on %s for output", out_cfg->pin, \
out_cfg->label); \
return err; \
} else { \
LOG_DBG("Configured pin %d on %s for output", out_cfg->pin, out_cfg->label); \
} \
} \
data->dev = dev; \
\
k_timer_init(&data->poll_timer, kscan_gpio_timer_handler, NULL); \
\
(CHECK_DEBOUNCE_CFG(n, (k_work_init), (k_delayed_work_init)))( \
&data->work, kscan_gpio_work_handler_##n); \
return 0; \
} \
\
static const struct kscan_driver_api gpio_driver_api_##n = { \
.config = kscan_gpio_configure_##n, \
.enable_callback = kscan_gpio_enable_##n, \
.disable_callback = kscan_gpio_disable_##n, \
}; \
\
static const struct kscan_gpio_config_##n kscan_gpio_config_##n = { \
.rows = {UTIL_LISTIFY(INST_MATRIX_INPUTS(n), _KSCAN_GPIO_INPUT_CFG_INIT, n)}, \
.cols = {UTIL_LISTIFY(INST_DEMUX_GPIOS(n), _KSCAN_GPIO_OUTPUT_CFG_INIT, n)}, \
}; \
\
DEVICE_AND_API_INIT(kscan_gpio_##n, DT_INST_LABEL(n), kscan_gpio_init_##n, \
&kscan_gpio_data_##n, &kscan_gpio_config_##n, APPLICATION, \
CONFIG_APPLICATION_INIT_PRIORITY, &gpio_driver_api_##n);
DT_INST_FOREACH_STATUS_OKAY(GPIO_INST_INIT)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

25
app/drivers/zephyr/kscan_gpio_matrix.c

@ -181,19 +181,18 @@ static int kscan_gpio_config_interrupts(struct device **devices,
struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \ struct kscan_gpio_data_##n *data = CONTAINER_OF(work, struct kscan_gpio_data_##n, work); \
kscan_gpio_read_##n(data->dev); \ kscan_gpio_read_##n(data->dev); \
} \ } \
COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \ static void kscan_gpio_irq_callback_handler_##n(struct device *dev, struct gpio_callback *cb, \
(static void kscan_gpio_irq_callback_handler_##n( \ gpio_port_pins_t pin) { \
struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pin) { \ struct kscan_gpio_irq_callback_##n *data = \
struct kscan_gpio_irq_callback_##n *data = \ CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \
CONTAINER_OF(cb, struct kscan_gpio_irq_callback_##n, callback); \ COND_CODE_1(CONFIG_ZMK_KSCAN_MATRIX_POLLING, (), \
kscan_gpio_disable_interrupts_##n(data->dev); \ (kscan_gpio_disable_interrupts_##n(data->dev);)) \
COND_CODE_0(DT_INST_PROP(n, debounce_period), \ COND_CODE_0(DT_INST_PROP(n, debounce_period), ({ k_work_submit(data->work); }), ({ \
({ k_work_submit(data->work); }), ({ \ k_delayed_work_cancel(data->work); \
k_delayed_work_cancel(data->work); \ k_delayed_work_submit(data->work, \
k_delayed_work_submit( \ K_MSEC(DT_INST_PROP(n, debounce_period))); \
data->work, K_MSEC(DT_INST_PROP(n, debounce_period))); \ })) \
})) \ } \
})) \
\ \
static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \ static struct kscan_gpio_data_##n kscan_gpio_data_##n = { \
.rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \ .rows = {[INST_MATRIX_ROWS(n) - 1] = NULL}, .cols = {[INST_MATRIX_COLS(n) - 1] = NULL}}; \

3
app/dts/behaviors.dtsi

@ -8,4 +8,5 @@
#include <behaviors/reset.dtsi> #include <behaviors/reset.dtsi>
#include <behaviors/sensor_rotate_key_press.dtsi> #include <behaviors/sensor_rotate_key_press.dtsi>
#include <behaviors/rgb_underglow.dtsi> #include <behaviors/rgb_underglow.dtsi>
#include <behaviors/bluetooth.dtsi> #include <behaviors/bluetooth.dtsi>
#include <behaviors/ext_power.dtsi>

9
app/dts/behaviors/ext_power.dtsi

@ -0,0 +1,9 @@
/ {
behaviors {
ext_power: behavior_ext_power {
compatible = "zmk,behavior-ext-power";
label = "EXT_POWER_BEHAVIOR";
#binding-cells = <1>;
};
};
};

10
app/dts/bindings/behaviors/zmk,behavior-ext-power.yaml

@ -0,0 +1,10 @@
#
# Copyright (c) 2020, The ZMK Contributors
# SPDX-License-Identifier: MIT
#
description: External power control Behavior
compatible: "zmk,behavior-ext-power"
include: one_param.yaml

2
app/include/dt-bindings/zmk/bt.h

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> * Copyright (c) 2020 The ZMK Contributors
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

13
app/include/dt-bindings/zmk/ext_power.h

@ -0,0 +1,13 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define EXT_POWER_OFF_CMD 0
#define EXT_POWER_ON_CMD 1
#define EXT_POWER_TOGGLE_CMD 2
#define EP_ON EXT_POWER_ON_CMD
#define EP_OFF EXT_POWER_OFF_CMD
#define EP_TOG EXT_POWER_TOGGLE_CMD

2
app/include/zmk/ble/profile.h

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> * Copyright (c) 2020 The ZMK Contributors
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

2
app/include/zmk/events/ble-active-profile-changed.h

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> * Copyright (c) 2020 The ZMK Contributors
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

2
app/src/behaviors/behavior_bt.c

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> * Copyright (c) 2020 The ZMK Contributors
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

57
app/src/behaviors/behavior_ext_power.c

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_ext_power
#include <device.h>
#include <devicetree.h>
#include <drivers/behavior.h>
#include <drivers/ext_power.h>
#include <dt-bindings/zmk/ext_power.h>
#include <logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
struct device *ext_power = device_get_binding("EXT_POWER");
if (ext_power == NULL) {
LOG_ERR("Unable to retrieve ext_power device: %d", binding->param1);
return -EIO;
}
switch (binding->param1) {
case EXT_POWER_OFF_CMD:
return ext_power_disable(ext_power);
case EXT_POWER_ON_CMD:
return ext_power_enable(ext_power);
case EXT_POWER_TOGGLE_CMD:
if (ext_power_get(ext_power) > 0)
return ext_power_disable(ext_power);
else
return ext_power_enable(ext_power);
default:
LOG_ERR("Unknown ext_power command: %d", binding->param1);
}
return -ENOTSUP;
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return 0;
}
static int behavior_ext_power_init(struct device *dev) { return 0; };
static const struct behavior_driver_api behavior_ext_power_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
};
DEVICE_AND_API_INIT(behavior_ext_power, DT_INST_LABEL(0), behavior_ext_power_init, NULL, NULL,
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);

2
app/src/behaviors/behavior_key_press.c

@ -54,4 +54,4 @@ static const struct behavior_driver_api behavior_key_press_driver_api = {
&behavior_key_press_data_##n, &behavior_key_press_config_##n, APPLICATION, \ &behavior_key_press_data_##n, &behavior_key_press_config_##n, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_press_driver_api); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST) DT_INST_FOREACH_STATUS_OKAY(KP_INST)

146
app/src/ble.c

@ -45,10 +45,29 @@ static u8_t passkey_digit = 0;
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED #define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
#endif #endif
enum advertising_type {
ZMK_ADV_NONE,
ZMK_ADV_DIR,
ZMK_ADV_CONN,
} advertising_status;
#define CURR_ADV(adv) (adv << 4)
#define ZMK_ADV_CONN_NAME \
BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_ONE_TIME, BT_GAP_ADV_FAST_INT_MIN_2, \
BT_GAP_ADV_FAST_INT_MAX_2, NULL)
static struct zmk_ble_profile profiles[PROFILE_COUNT]; static struct zmk_ble_profile profiles[PROFILE_COUNT];
static u8_t active_profile; static u8_t active_profile;
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
static const struct bt_data zmk_ble_ad[] = { static const struct bt_data zmk_ble_ad[] = {
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0xC1, 0x03),
#endif
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID16_SOME, BT_DATA_BYTES(BT_DATA_UUID16_SOME,
#if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) #if !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL)
@ -92,29 +111,101 @@ void set_profile_address(u8_t index, const bt_addr_le_t *addr) {
raise_profile_changed_event(); raise_profile_changed_event();
} }
int zmk_ble_adv_pause() { bool active_profile_is_connected() {
int err = bt_le_adv_stop(); struct bt_conn *conn;
if (err) { bt_addr_le_t *addr = zmk_ble_active_profile_addr();
LOG_ERR("Failed to stop advertising (err %d)", err); if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
return err; return false;
} else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) {
return false;
} }
return 0; bt_conn_unref(conn);
};
int zmk_ble_adv_resume() { return true;
LOG_DBG("active_profile %d, directed? %s", active_profile, }
active_profile_is_open() ? "no" : "yes");
int err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0); #define CHECKED_ADV_STOP() \
if (err) { err = bt_le_adv_stop(); \
LOG_ERR("Advertising failed to start (err %d)", err); advertising_status = ZMK_ADV_NONE; \
return err; if (err) { \
LOG_ERR("Failed to stop advertising (err %d)", err); \
return err; \
}
#define CHECKED_DIR_ADV() \
addr = zmk_ble_active_profile_addr(); \
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr); \
if (conn != NULL) { /* TODO: Check status of connection */ \
LOG_DBG("Skipping advertising, profile host is already connected"); \
bt_conn_unref(conn); \
return 0; \
} \
err = bt_le_adv_start(BT_LE_ADV_CONN_DIR_LOW_DUTY(addr), zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), \
NULL, 0); \
if (err) { \
LOG_ERR("Advertising failed to start (err %d)", err); \
return err; \
} \
advertising_status = ZMK_ADV_DIR;
#define CHECKED_OPEN_ADV() \
err = bt_le_adv_start(ZMK_ADV_CONN_NAME, zmk_ble_ad, ARRAY_SIZE(zmk_ble_ad), NULL, 0); \
if (err) { \
LOG_ERR("Advertising failed to start (err %d)", err); \
return err; \
} \
advertising_status = ZMK_ADV_CONN;
int update_advertising() {
int err = 0;
bt_addr_le_t *addr;
struct bt_conn *conn;
enum advertising_type desired_adv = ZMK_ADV_NONE;
if (active_profile_is_open() || !active_profile_is_connected()) {
desired_adv = ZMK_ADV_CONN;
} else if (!active_profile_is_connected()) {
desired_adv = ZMK_ADV_CONN;
// Need to fix directed advertising for privacy centrals. See
// https://github.com/zephyrproject-rtos/zephyr/pull/14984 char
// addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(zmk_ble_active_profile_addr(), addr_str,
// sizeof(addr_str));
// LOG_DBG("Directed advertising to %s", log_strdup(addr_str));
// desired_adv = ZMK_ADV_DIR;
}
LOG_DBG("advertising from %d to %d", advertising_status, desired_adv);
switch (desired_adv + CURR_ADV(advertising_status)) {
case ZMK_ADV_NONE + CURR_ADV(ZMK_ADV_DIR):
case ZMK_ADV_NONE + CURR_ADV(ZMK_ADV_CONN):
CHECKED_ADV_STOP();
break;
case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_DIR):
case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_CONN):
CHECKED_ADV_STOP();
CHECKED_DIR_ADV();
break;
case ZMK_ADV_DIR + CURR_ADV(ZMK_ADV_NONE):
CHECKED_DIR_ADV();
break;
case ZMK_ADV_CONN + CURR_ADV(ZMK_ADV_DIR):
CHECKED_ADV_STOP();
CHECKED_OPEN_ADV();
break;
case ZMK_ADV_CONN + CURR_ADV(ZMK_ADV_NONE):
CHECKED_OPEN_ADV();
break;
} }
return 0; return 0;
}; };
static void update_advertising_callback(struct k_work *work) { update_advertising(); }
K_WORK_DEFINE(update_advertising_work, update_advertising_callback);
int zmk_ble_clear_bonds() { int zmk_ble_clear_bonds() {
LOG_DBG(""); LOG_DBG("");
@ -124,6 +215,8 @@ int zmk_ble_clear_bonds() {
set_profile_address(active_profile, BT_ADDR_LE_ANY); set_profile_address(active_profile, BT_ADDR_LE_ANY);
} }
update_advertising();
return 0; return 0;
}; };
@ -134,9 +227,13 @@ int zmk_ble_prof_select(u8_t index) {
} }
active_profile = index; active_profile = index;
return settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile)); settings_save_one("ble/active_profile", &active_profile, sizeof(active_profile));
update_advertising();
raise_profile_changed_event(); raise_profile_changed_event();
return 0;
}; };
int zmk_ble_prof_next() { int zmk_ble_prof_next() {
@ -234,8 +331,11 @@ static void connected(struct bt_conn *conn, u8_t err) {
char addr[BT_ADDR_LE_STR_LEN]; char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
advertising_status = ZMK_ADV_NONE;
if (err) { if (err) {
LOG_WRN("Failed to connect to %s (%u)", log_strdup(addr), err); LOG_WRN("Failed to connect to %s (%u)", log_strdup(addr), err);
update_advertising();
return; return;
} }
@ -250,6 +350,8 @@ static void connected(struct bt_conn *conn, u8_t err) {
if (bt_conn_set_security(conn, BT_SECURITY_L2)) { if (bt_conn_set_security(conn, BT_SECURITY_L2)) {
LOG_ERR("Failed to set security"); LOG_ERR("Failed to set security");
} }
update_advertising();
} }
static void disconnected(struct bt_conn *conn, u8_t reason) { static void disconnected(struct bt_conn *conn, u8_t reason) {
@ -259,14 +361,9 @@ static void disconnected(struct bt_conn *conn, u8_t reason) {
LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason); LOG_DBG("Disconnected from %s (reason 0x%02x)", log_strdup(addr), reason);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) // We need to do this in a work callback, otherwise the advertising update will still see the
// if (bt_addr_le_cmp(&peripheral_addr, BT_ADDR_LE_ANY) && bt_addr_le_cmp(&peripheral_addr, // connection for a profile as active, and not start advertising yet.
// bt_conn_get_dst(conn))) { k_work_submit(&update_advertising_work);
// zmk_ble_adv_resume();
// }
#else
// zmk_ble_adv_resume();
#endif
} }
static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) { static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err) {
@ -361,6 +458,7 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */ #endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
set_profile_address(active_profile, dst); set_profile_address(active_profile, dst);
update_advertising();
}; };
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = { static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
@ -383,7 +481,7 @@ static void zmk_ble_ready(int err) {
return; return;
} }
zmk_ble_adv_resume(); update_advertising();
} }
static int zmk_ble_init(struct device *_arg) { static int zmk_ble_init(struct device *_arg) {

2
app/src/events/ble_active_profile_changed.c

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 Peter Johanson <peter@peterjohanson.com> * Copyright (c) 2020 The ZMK Contributors
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */

12
app/src/hog.c

@ -164,8 +164,10 @@ int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report) {
LOG_DBG("Sending to NULL? %s", conn == NULL ? "yes" : "no"); LOG_DBG("Sending to NULL? %s", conn == NULL ? "yes" : "no");
return bt_gatt_notify(conn, &hog_svc.attrs[5], report, int err =
sizeof(struct zmk_hid_keypad_report_body)); bt_gatt_notify(conn, &hog_svc.attrs[5], report, sizeof(struct zmk_hid_keypad_report_body));
bt_conn_unref(conn);
return err;
}; };
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) { int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
@ -174,6 +176,8 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
return -ENOTCONN; return -ENOTCONN;
} }
return bt_gatt_notify(conn, &hog_svc.attrs[10], report, int err = bt_gatt_notify(conn, &hog_svc.attrs[10], report,
sizeof(struct zmk_hid_consumer_report_body)); sizeof(struct zmk_hid_consumer_report_body));
bt_conn_unref(conn);
return err;
}; };

2
app/src/kscan_mock.c

@ -18,7 +18,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct kscan_mock_data { struct kscan_mock_data {
kscan_callback_t callback; kscan_callback_t callback;
u8_t event_index; u32_t event_index;
struct k_delayed_work work; struct k_delayed_work work;
struct device *dev; struct device *dev;
}; };

95
app/src/rgb_underglow.c

@ -7,6 +7,7 @@
#include <device.h> #include <device.h>
#include <init.h> #include <init.h>
#include <kernel.h> #include <kernel.h>
#include <settings/settings.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
@ -14,7 +15,6 @@
#include <logging/log.h> #include <logging/log.h>
#include <drivers/led_strip.h> #include <drivers/led_strip.h>
#include <device.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -45,12 +45,36 @@ struct rgb_underglow_state {
bool on; bool on;
}; };
struct rgb_underglow_state state;
struct device *led_strip; struct device *led_strip;
struct led_rgb pixels[STRIP_NUM_PIXELS]; struct led_rgb pixels[STRIP_NUM_PIXELS];
struct rgb_underglow_state state;
#if IS_ENABLED(CONFIG_SETTINGS)
static int rgb_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {
const char *next;
int rc;
if (settings_name_steq(name, "state", &next) && !next) {
if (len != sizeof(state)) {
return -EINVAL;
}
rc = read_cb(cb_arg, &state, sizeof(state));
if (rc >= 0) {
return 0;
}
return rc;
}
return -ENOENT;
}
struct settings_handler rgb_conf = {.name = "rgb/underglow", .h_set = rgb_settings_set};
#endif
static struct led_rgb hsb_to_rgb(struct led_hsb hsb) { static struct led_rgb hsb_to_rgb(struct led_hsb hsb) {
double r, g, b; double r, g, b;
@ -100,6 +124,14 @@ static struct led_rgb hsb_to_rgb(struct led_hsb hsb) {
return rgb; return rgb;
} }
static void zmk_rgb_underglow_off() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
}
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
}
static void zmk_rgb_underglow_effect_solid() { static void zmk_rgb_underglow_effect_solid() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) { for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = state.hue; int hue = state.hue;
@ -182,6 +214,14 @@ static void zmk_rgb_underglow_tick(struct k_work *work) {
K_WORK_DEFINE(underglow_work, zmk_rgb_underglow_tick); K_WORK_DEFINE(underglow_work, zmk_rgb_underglow_tick);
static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) { static void zmk_rgb_underglow_tick_handler(struct k_timer *timer) {
if (!state.on) {
zmk_rgb_underglow_off();
k_timer_stop(timer);
return;
}
k_work_submit(&underglow_work); k_work_submit(&underglow_work);
} }
@ -197,20 +237,32 @@ static int zmk_rgb_underglow_init(struct device *_arg) {
} }
state = (struct rgb_underglow_state){ state = (struct rgb_underglow_state){
hue : 0, hue : CONFIG_ZMK_RGB_UNDERGLOW_HUE_START,
saturation : 100, saturation : CONFIG_ZMK_RGB_UNDERGLOW_SAT_START,
brightness : 100, brightness : CONFIG_ZMK_RGB_UNDERGLOW_BRT_START,
animation_speed : 3, animation_speed : CONFIG_ZMK_RGB_UNDERGLOW_SPD_START,
current_effect : 0, current_effect : CONFIG_ZMK_RGB_UNDERGLOW_EFF_START,
animation_step : 0, animation_step : 0,
on : true on : IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_ON_START)
}; };
#if IS_ENABLED(CONFIG_SETTINGS)
settings_register(&rgb_conf);
#endif
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50)); k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
return 0; return 0;
} }
int zmk_rgb_underglow_save_state() {
#if IS_ENABLED(CONFIG_SETTINGS)
return settings_save_one("rgb/underglow/state", &state, sizeof(state));
#else
return 0;
#endif
}
int zmk_rgb_underglow_cycle_effect(int direction) { int zmk_rgb_underglow_cycle_effect(int direction) {
if (!led_strip) if (!led_strip)
return -ENODEV; return -ENODEV;
@ -228,7 +280,7 @@ int zmk_rgb_underglow_cycle_effect(int direction) {
state.animation_step = 0; state.animation_step = 0;
return 0; return zmk_rgb_underglow_save_state();
} }
int zmk_rgb_underglow_toggle() { int zmk_rgb_underglow_toggle() {
@ -241,17 +293,12 @@ int zmk_rgb_underglow_toggle() {
state.animation_step = 0; state.animation_step = 0;
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50)); k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
} else { } else {
zmk_rgb_underglow_off();
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
}
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
k_timer_stop(&underglow_tick); k_timer_stop(&underglow_tick);
} }
return 0; return zmk_rgb_underglow_save_state();
} }
int zmk_rgb_underglow_change_hue(int direction) { int zmk_rgb_underglow_change_hue(int direction) {
@ -259,17 +306,15 @@ int zmk_rgb_underglow_change_hue(int direction) {
return -ENODEV; return -ENODEV;
if (state.hue == 0 && direction < 0) { if (state.hue == 0 && direction < 0) {
state.hue = 350; state.hue = 360 - CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
return 0; return 0;
} }
state.hue += direction * CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP; state.hue += direction * CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
if (state.hue > 350) { state.hue = state.hue % 360;
state.hue = 0;
}
return 0; return zmk_rgb_underglow_save_state();
} }
int zmk_rgb_underglow_change_sat(int direction) { int zmk_rgb_underglow_change_sat(int direction) {
@ -286,7 +331,7 @@ int zmk_rgb_underglow_change_sat(int direction) {
state.saturation = 100; state.saturation = 100;
} }
return 0; return zmk_rgb_underglow_save_state();
} }
int zmk_rgb_underglow_change_brt(int direction) { int zmk_rgb_underglow_change_brt(int direction) {
@ -303,7 +348,7 @@ int zmk_rgb_underglow_change_brt(int direction) {
state.brightness = 100; state.brightness = 100;
} }
return 0; return zmk_rgb_underglow_save_state();
} }
int zmk_rgb_underglow_change_spd(int direction) { int zmk_rgb_underglow_change_spd(int direction) {
@ -320,7 +365,7 @@ int zmk_rgb_underglow_change_spd(int direction) {
state.animation_speed = 5; state.animation_speed = 5;
} }
return 0; return zmk_rgb_underglow_save_state();
} }
SYS_INIT(zmk_rgb_underglow_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); SYS_INIT(zmk_rgb_underglow_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

8
app/src/settings.c

@ -0,0 +1,8 @@
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <settings/settings.h>
static int zmk_settings_init(struct device *_arg) { return settings_load(); }
SYS_INIT(zmk_settings_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

2
docs/.eslintignore

@ -0,0 +1,2 @@
node_modules
build

29
docs/.eslintrc.js

@ -0,0 +1,29 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:mdx/recommended",
"prettier",
"prettier/react",
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2021,
sourceType: "module",
},
plugins: ["react"],
rules: {},
settings: {
react: {
version: "detect",
},
},
};

3
docs/.prettierignore

@ -0,0 +1,3 @@
node_modules
build
.docusaurus

3
docs/.prettierrc.js

@ -0,0 +1,3 @@
module.exports = {
endOfLine: "auto",
};

5
docs/README.md

@ -2,6 +2,10 @@
This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator.
## License
The ZMK Documentation is licensed [CC-BY-NC-SA](http://creativecommons.org/licenses/by-nc-sa/4.0/).
### Installation ### Installation
``` ```
@ -23,4 +27,3 @@ $ npm build
``` ```
This command generates static content into the `build` directory and can be served using any static contents hosting service. This command generates static content into the `build` directory and can be served using any static contents hosting service.

1
docs/blog/2020-05-24-wip.md

@ -1,5 +1,4 @@
--- ---
id: wip
title: WIP title: WIP
author: Pete Johanson author: Pete Johanson
author_title: Project Creator author_title: Project Creator

15
docs/docs/behavior/hold-tap.md

@ -4,17 +4,18 @@ sidebar_label: Hold-Tap
--- ---
## Summary ## Summary
Hold-tap is the basis for other behaviors such as layer-tap and mod-tap.
Simply put, the hold-tap key will output the 'hold' behavior if it's held for a while, and output the 'tap' behavior when it's tapped quickly. Hold-tap is the basis for other behaviors such as layer-tap and mod-tap.
Simply put, the hold-tap key will output the 'hold' behavior if it's held for a while, and output the 'tap' behavior when it's tapped quickly.
### Hold-Tap ### Hold-Tap
The `tapping_term_ms` parameter decides between a 'tap' and a 'hold'. The `tapping_term_ms` parameter decides between a 'tap' and a 'hold'.
![Simple behavior](../assets/hold-tap/case1_2.png) ![Simple behavior](../assets/hold-tap/case1_2.png)
By default, the hold-tap is configured to also select the 'hold' functionality if another key is tapped while it's active: By default, the hold-tap is configured to also select the 'hold' functionality if another key is tapped while it's active:
![Hold preferred behavior](../assets/hold-tap/case1_2.png) ![Hold preferred behavior](../assets/hold-tap/case1_2.png)
@ -23,10 +24,11 @@ We call this the 'hold-preferred' flavor of hold-taps. While this flavor may wor
![Hold-tap comparison](../assets/hold-tap/comparison.png) ![Hold-tap comparison](../assets/hold-tap/comparison.png)
### Basic usage ### Basic usage
For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md) pages.
For basic usage, please see [mod-tap](./mod-tap.md) and [layer-tap](./layers.md) pages.
### Advanced Configuration ### Advanced Configuration
A code example which configures a mod-tap setting that works with homerow mods: A code example which configures a mod-tap setting that works with homerow mods:
``` ```
@ -44,7 +46,7 @@ A code example which configures a mod-tap setting that works with homerow mods:
bindings = <&kp>, <&kp>; bindings = <&kp>, <&kp>;
}; };
}; };
keymap { keymap {
compatible = "zmk,keymap"; compatible = "zmk,keymap";
@ -63,4 +65,5 @@ If this config does not work for you, try the flavor "tap-preferred" and a short
If you want to use a tap-hold with a keycode from a different code page, you have to define another behavior with another "bindings" parameter.For example, if you want to use SHIFT and volume up, define the bindings like `bindings = <&kp>, <&cp>;`. Only single-argument behaviors are supported at the moment. If you want to use a tap-hold with a keycode from a different code page, you have to define another behavior with another "bindings" parameter.For example, if you want to use SHIFT and volume up, define the bindings like `bindings = <&kp>, <&cp>;`. Only single-argument behaviors are supported at the moment.
#### Comparison to QMK #### Comparison to QMK
The hold-preferred flavor works similar to the `HOLD_ON_OTHER_KEY_PRESS` setting in QMK. The 'balanced' flavor is similar to the `PERMISSIVE_HOLD` setting, and the `tap-preferred` flavor is similar to `IGNORE_MOD_TAP_INTERRUPT`.
The hold-preferred flavor works similar to the `HOLD_ON_OTHER_KEY_PRESS` setting in QMK. The 'balanced' flavor is similar to the `PERMISSIVE_HOLD` setting, and the `tap-preferred` flavor is similar to `IGNORE_MOD_TAP_INTERRUPT`.

2
docs/docs/behavior/key-press.md

@ -64,4 +64,4 @@ Example:
``` ```
&cp M_PREV &cp M_PREV
``` ```

2
docs/docs/behavior/layers.md

@ -46,6 +46,7 @@ Example:
The "layer-tap" behavior enables a layer when a key is held, and output another key when the key is only tapped for a short time. For more information on the inner workings of layer-tap, see [hold-tap](./hold-tap.md). The "layer-tap" behavior enables a layer when a key is held, and output another key when the key is only tapped for a short time. For more information on the inner workings of layer-tap, see [hold-tap](./hold-tap.md).
### Behavior Binding ### Behavior Binding
- Reference: `&lt` - Reference: `&lt`
- Parameter: The layer number to enable when held, e.g. `1` - Parameter: The layer number to enable when held, e.g. `1`
- Parameter: The keycode to send when tapped, e.g. `A` - Parameter: The keycode to send when tapped, e.g. `A`
@ -56,7 +57,6 @@ Example:
&lt LOWER SPC &lt LOWER SPC
``` ```
## Toggle Layer ## Toggle Layer
The "toggle layer" behavior enables a layer until the layer is manually disabled. The "toggle layer" behavior enables a layer until the layer is manually disabled.

1
docs/docs/behavior/mod-tap.md

@ -40,4 +40,3 @@ You can configure a different tapping term in your keymap:
} }
} }
``` ```

64
docs/docs/behavior/power.md

@ -0,0 +1,64 @@
---
title: Power Management Behaviors
sidebar_label: Power Management
---
## Summary
These page contains some of the power management behaviors currently supported by ZMK.
## External Power Control
The External power control behavior allows enabling or disabling the VCC power output
to save power. Some of the LEDs will consume power even in OFF state. To preserve
battery life in this scenario, some controller boards have support to disable the
external power completely.
The following boards currently support this feature:
- nRFMicro
- nice!nano
## External Power Control Command Defines
External power control command defines are provided through the [`dt-bindings/zmk/ext_power.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/ext_power.h) header,
which is added at the top of the keymap file:
```
#include <dt-bindings/zmk/ext_power.h>
```
This will allow you to reference the actions defined in this header such as `EXT_POWER_OFF_CMD`.
Here is a table describing the command for each define:
| Define | Action | Alias |
| ---------------------- | --------------------------- | -------- |
| `EXT_POWER_OFF_CMD` | Disable the external power. | `EP_OFF` |
| `EXT_POWER_ON_CMD` | Enable the external power. | `EP_ON` |
| `EXT_POWER_TOGGLE_CMD` | Toggle the external power. | `EP_TOG` |
### Behavior Binding
- Reference: `&ext_power`
- Parameter#1: Command, e.g `EP_ON`
### Example:
1. Behavior binding to enable the external power
```
&ext_power EP_ON
```
1. Behavior binding to disable the external power
```
&ext_power EP_OFF
```
1. Behavior binding to toggle the external power
```
&ext_power EP_TOG
```

3
docs/docs/bond-reset.md

@ -17,7 +17,6 @@ list, you will need to clear the bonds.
Split keyboards will need to be cleared on both halves. For best results try to reset them at the same time. Split keyboards will need to be cleared on both halves. For best results try to reset them at the same time.
### Kyria ### Kyria
![Kyria bond-reset combo](assets/bond-clearing/kyria.jpg) ![Kyria bond-reset combo](assets/bond-clearing/kyria.jpg)
@ -28,4 +27,4 @@ Split keyboards will need to be cleared on both halves. For best results try to
### Lily58 ### Lily58
![Lily58 bond-reset combo](assets/bond-clearing/lily58.jpg) ![Lily58 bond-reset combo](assets/bond-clearing/lily58.jpg)

28
docs/docs/customization.md

@ -1,10 +1,22 @@
--- ---
id: customization id: customization
title: Customizing ZMK title: Customizing ZMK/`zmk-config` folders
sidebar_label: Customizing ZMK sidebar_label: Customizing ZMK
--- ---
After verifying you can successfully flash the default firmware, you will probably want to begin customizing your keymap and other keyboard options. After verifying you can successfully flash the default firmware, you will probably want to begin customizing your keymap and other keyboard options.
[In the initial setup tutorial](user-setup), you created a Github repository called `zmk-config`. This repository is a discrete filesystem which works
with the main `zmk` firmware repository to build your desired firmware. The main advantage of a discrete configuration folder is ensuring that the
working components of ZMK are kept separate from your personal keyboard settings, reducing the amount of file manipulation in the configuration process.
This makes flashing ZMK to your keyboard much easier, especially because you don't need to keep an up-to-date copy of zmk on your computer at all times.
On default `zmk-config` folder should contain two files:
- `<shield>.conf`
- `<shield>`.keymap
However, your config folder can also be modified to include a `boards/` directory for keymaps and configurations for multiple boards/shields
outside of the default keyboard setting definitions.
## Configuration Changes ## Configuration Changes
@ -26,8 +38,22 @@ GitHub Actions job to build your firmware which you can download once it complet
If you need to, a review of [Learn The Basics Of Git In Under 10 Minutes](https://www.freecodecamp.org/news/learn-the-basics-of-git-in-under-10-minutes-da548267cc91/) will help you get these steps right. If you need to, a review of [Learn The Basics Of Git In Under 10 Minutes](https://www.freecodecamp.org/news/learn-the-basics-of-git-in-under-10-minutes-da548267cc91/) will help you get these steps right.
::: :::
## Building from a local `zmk` fork using `zmk-config`
[As outlined here](dev-build-flash), firmware comes in the form of .uf2 files, which can be built locally using the command `west build`. Normally,
`west build` will default to using the in-tree .keymap and .conf files found in your local copy of the `zmk` repository. However, you can append the command, `-DZMK_CONFIG="C:/the/absolute/path/config"` to `west build` in order to use the contents of your `zmk-config` folder instead of the
default keyboard settings.
**Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this:
```
west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Documents/Github/zmk-config/config"
```
## Flashing Your Changes ## Flashing Your Changes
For normal keyboards, follow the same flashing instructions as before to flash your updated firmware. For normal keyboards, follow the same flashing instructions as before to flash your updated firmware.
For split keyboards, only the central (left) side will need to be reflashed if you are just updating your keymap. For split keyboards, only the central (left) side will need to be reflashed if you are just updating your keymap.
More troubleshooting information for split keyboards can be found [here](troubleshooting#split-keyboard-halves-unable-to-pair).

9
docs/docs/dev-build.md

@ -62,11 +62,13 @@ west build -b planck_rev6
``` ```
### Pristine Building ### Pristine Building
When building for a new board and/or shield after having built one previously, you may need to enable the pristine build option. This option removes all existing files in the build directory before regenerating them, and can be enabled by adding either --pristine or -p to the command: When building for a new board and/or shield after having built one previously, you may need to enable the pristine build option. This option removes all existing files in the build directory before regenerating them, and can be enabled by adding either --pristine or -p to the command:
```sh ```sh
west build -p -b proton_c -- -DSHIELD=kyria_left west build -p -b proton_c -- -DSHIELD=kyria_left
``` ```
### Building For Split Keyboards ### Building For Split Keyboards
:::note :::note
@ -78,25 +80,26 @@ By default, the `build` command outputs a single .uf2 file named `zmk.uf2` so bu
``` ```
west build -d build/left -b nice_nano -- -DSHIELD=kyria_left west build -d build/left -b nice_nano -- -DSHIELD=kyria_left
``` ```
and then building right into `build/right`: and then building right into `build/right`:
``` ```
west build -d build/right -b nice_nano -- -DSHIELD=kyria_right west build -d build/right -b nice_nano -- -DSHIELD=kyria_right
``` ```
This produces `left` and `right` subfolders under the `build` directory and two separate .uf2 files. For future work on a specific half, use the `-d` parameter again to ensure you are building into the correct location. This produces `left` and `right` subfolders under the `build` directory and two separate .uf2 files. For future work on a specific half, use the `-d` parameter again to ensure you are building into the correct location.
### Building from `zmk-config` Folder ### Building from `zmk-config` Folder
Instead of building .uf2 files using the default keymap and config files, you can build directly from your [`zmk-config` folder](user-setup#github-repo) by adding Instead of building .uf2 files using the default keymap and config files, you can build directly from your [`zmk-config` folder](user-setup#github-repo) by adding
`-DZMK_CONFIG="C:/the/absolute/path/config"` to your `west build` command. **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.** `-DZMK_CONFIG="C:/the/absolute/path/config"` to your `west build` command. **Notice that this path should point to the folder labelled `config` within your `zmk-config` folder.**
For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this: For instance, building kyria firmware from a user `myUser`'s `zmk-config` folder on Windows 10 may look something like this:
``` ```
west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Documents/Github/zmk-config/config" west build -b nice_nano -- -DSHIELD=kyria_left -DZMK_CONFIG="C:/Users/myUser/Documents/Github/zmk-config/config"
``` ```
## Flashing ## Flashing
Once built, the previously supplied parameters will be remembered so you can run the following to flash your Once built, the previously supplied parameters will be remembered so you can run the following to flash your

198
docs/docs/dev-guide-new-shield.md

@ -13,7 +13,7 @@ The high level steps are:
- Create a new shield directory. - Create a new shield directory.
- Add the base Kconfig files. - Add the base Kconfig files.
- Add the shield overlay file to define the [KSCAN driver]() for detecting key press/release. - Add the shield overlay file to define the KSCAN driver for detecting key press/release.
- (Optional) Add the matrix transform for mapping KSCAN row/column values to sane key positions. This is needed for non-rectangular keyboards, or where the underlying row/column pin arrangement does not map one to one with logical locations on the keyboard. - (Optional) Add the matrix transform for mapping KSCAN row/column values to sane key positions. This is needed for non-rectangular keyboards, or where the underlying row/column pin arrangement does not map one to one with logical locations on the keyboard.
- Add a default keymap, which users can override in their own configs as needed. - Add a default keymap, which users can override in their own configs as needed.
- Add support for features such as encoders, OLED displays, or RGB underglow. - Add support for features such as encoders, OLED displays, or RGB underglow.
@ -21,6 +21,10 @@ The high level steps are:
It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing. It may be helpful to review the upstream [shields documentation](https://docs.zephyrproject.org/2.3.0/guides/porting/shields.html#shields) to get a proper understanding of the underlying system before continuing.
:::note
ZMK support for split keyboards requires a few more files than single boards to ensure proper connectivity between the central and peripheral units. Check the following guides thoroughly to ensure that all the files are in place.
:::
## New Shield Directory ## New Shield Directory
Shields for Zephyr applications go into the `boards/shields/` directory; since ZMK's Zephyr application lives in the `app/` subdirectory of the repository, that means the new shield directory should be: Shields for Zephyr applications go into the `boards/shields/` directory; since ZMK's Zephyr application lives in the `app/` subdirectory of the repository, that means the new shield directory should be:
@ -45,6 +49,16 @@ config SHIELD_MY_BOARD
This will make sure the new configuration `SHIELD_MY_BOARD` is set to true whenever `my_board` is added as a shield in your build. This will make sure the new configuration `SHIELD_MY_BOARD` is set to true whenever `my_board` is added as a shield in your build.
**For split boards**, you will need to add configurations for the left and right sides.
```
config SHIELD_MY_BOARD_LEFT
def_bool $(shields_list_contains,my_board_left)
config SHIELD_MY_BOARD_RIGHT
def_bool $(shields_list_contains,my_board_right)
```
### Kconfig.defconfig ### Kconfig.defconfig
The `Kconfig.defconfig` file is where overrides for various configuration settings The `Kconfig.defconfig` file is where overrides for various configuration settings
@ -63,13 +77,40 @@ config ZMK_KEYBOARD_NAME
endif endif
``` ```
## Shield Overlay Similarly to defining the halves of a split board in `Kconfig.shield` it is important to set the `ZMK_KEYBOARD_NAME` for each half of a split keyboard.
```
if SHIELD_MY_BOARD_LEFT
config ZMK_KEYBOARD_NAME
default "My Awesome Keyboard Left"
endif
if SHIELD_MY_BOARD_RIGHT
config ZMK_KEYBOARD_NAME
default "My Awesome Keyboard Right"
endif
```
## Shield Overlays
![Labelled Pro Micro pins](assets/pro-micro/pro-micro-pins-labelled.jpg) ![Labelled Pro Micro pins](assets/pro-micro/pro-micro-pins-labelled.jpg)
ZMK uses the green color coded pin names to generate devicetree node references. For example, to refer to the node `D0` in the devicetree files, use `&pro_micro_d 0` or to refer to `A1`, use `&pro_micro_a 1`. ZMK uses the green color coded pin names to generate devicetree node references. For example, to refer to the node `D0` in the devicetree files, use `&pro_micro_d 0` or to refer to `A1`, use `&pro_micro_a 1`.
The `<shield_name>.overlay` is the devicetree description of the keyboard shield that is merged with the primary board devicetree description before the build. For ZMK, this file at a minimum should include the [chosen]() node named `zmk,kscan` that references a KSCAN driver instance. For a simple 3x3 macropad matrix, <Tabs
defaultValue="unibody"
values={[
{label: 'Unibody Shields', value: 'unibody'},
{label: 'Split Shields', value: 'split'},
]}>
<TabItem value="unibody">
The `<shield_name>.overlay` is the devicetree description of the keyboard shield that is merged with the primary board devicetree description before the build. For ZMK, this file at a minimum should include the chosen node named `zmk,kscan` that references a KSCAN driver instance. For a simple 3x3 macropad matrix,
this might look something like: this might look something like:
``` ```
@ -98,6 +139,146 @@ this might look something like:
}; };
``` ```
</TabItem>
<TabItem value="split">
### .dtsi files and Shield Overlays (Split Shields)
Unlike unibody keyboards, split keyboards have a core .dtsi file with shield overlays for each half of the keyboard.
It is preferred to define only the `col-gpios` or `row-gpios` in the common shield .dtsi, depending on the `diode-direction` value.
For `col2row` directed boards like the iris, the shared .dtsi file may look like this:
```
#include <dt-bindings/zmk/matrix-transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <16>;
rows = <4>;
// | SW6 | SW5 | SW4 | SW3 | SW2 | SW1 | | SW1 | SW2 | SW3 | SW4 | SW5 | SW6 |
// | SW12 | SW11 | SW10 | SW9 | SW8 | SW7 | | SW7 | SW8 | SW9 | SW10 | SW11 | SW12 |
// | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 | | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 |
// | SW24 | SW23 | SW22 | SW21 | SW20 | SW19 | SW25 | | SW25 | SW19 | SW20 | SW21 | SW22 | SW23 | SW24 |
// | SW29 | SW28 | SW27 | SW26 | | SW26 | SW27 | SW28 | SW29 |
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,5) RC(0,6) RC(0,7) RC(0,8) RC(0,9) RC(0,10) RC(0,11)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,6) RC(1,7) RC(1,8) RC(1,9) RC(1,10) RC(1,11)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,6) RC(2,7) RC(2,8) RC(2,9) RC(2,10) RC(2,11)
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(4,2) RC(4,9) RC(3,6) RC(3,7) RC(3,8) RC(3,9) RC(3,10) RC(3,11)
RC(4,3) RC(4,4) RC(4,5) RC(4,6) RC(4,7) RC(4,8)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&pro_micro_d 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row A from the schematic file
, <&pro_micro_d 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row B from the schematic file
, <&pro_micro_d 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row C from the schematic file
, <&pro_micro_d 0 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row D from the schematic file
, <&pro_micro_d 4 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // Row E from the schematic file
;
};
```
:::note
Notice that in addition to the common `row-gpios` that are declared in the kscan, the [matrix transform](#optional-matrix-transform) is defined in the .dtsi.
:::
The missing `col-gpios` would be defined in your `<boardname>_left.overlay` and `<boardname>_right.overlay` files.
Keep in mind that the mirrored position of the GPIOs means that the `col-gpios` will appear reversed when the .overlay files are compared to one another.
Furthermore, the column offset for the [matrix transform](#optional-matrix-transform) should be added to the right half of the keyboard's overlay
because the keyboard's switch matrix is read from left to right, top to bottom.
This is exemplified with the iris .overlay files.
```
// iris_left.overlay
#include "iris.dtsi" // Notice that the main dtsi files are included in the overlay.
&kscan0 {
col-gpios
= <&pro_micro_a 1 GPIO_ACTIVE_HIGH> // col1 in the schematic
, <&pro_micro_a 0 GPIO_ACTIVE_HIGH> // col2 in the schematic
, <&pro_micro_d 15 GPIO_ACTIVE_HIGH> // col3 in the schematic
, <&pro_micro_d 14 GPIO_ACTIVE_HIGH> // col4 in the schematic
, <&pro_micro_d 16 GPIO_ACTIVE_HIGH> // col5 in the schematic
, <&pro_micro_d 10 GPIO_ACTIVE_HIGH> // col6 in the schematic
;
};
```
```
// iris_right.overlay
#include "iris.dtsi"
&default_transform { // The matrix transform for this board is 6 columns over because the left half is 6 columns wide according to the matrix.
col-offset = <6>;
};
&kscan0 {
col-gpios
= <&pro_micro_d 10 GPIO_ACTIVE_HIGH> // col6 in the schematic
, <&pro_micro_d 16 GPIO_ACTIVE_HIGH> // col5 in the schematic
, <&pro_micro_d 14 GPIO_ACTIVE_HIGH> // col4 in the schematic
, <&pro_micro_d 15 GPIO_ACTIVE_HIGH> // col3 in the schematic
, <&pro_micro_a 0 GPIO_ACTIVE_HIGH> // col2 in the schematic
, <&pro_micro_a 1 GPIO_ACTIVE_HIGH> // col1 in the schematic
;
};
```
### .conf files (Split Shields)
While unibody boards only have one .conf file that applies configuration characteristics to the entire keyboard,
split keyboards are unique in that they contain multiple .conf files with different scopes.
For example, a split board called `my_awesome_split_board` would have the following files:
- `my_awesome_split_board.conf` - Configuration elements affect both halves
- `my_awesome_split_board_left.conf` - Configuration elements only affect left half
- `my_awesome_split_board_right.conf` - Configuration elements only affect right half
For proper communication between keyboard halves and that between the central half and the computer,
the **the central and peripheral halves of the keyboard must be defined**. This can be seen below.
```
// Central Half (Usually the left side: my_awesome_split_board_left.conf)
CONFIG_ZMK_SPLIT=y
CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL=y
```
```
// Peripheral Half (Usually the right side: my_awesome_split_board_right.conf)
CONFIG_ZMK_SPLIT=y
CONFIG_ZMK_SPLIT_BLE_ROLE_Peripheral=y
```
Using the .conf file that affects both halves of a split board would be for adding features like deep-sleep or rotary encoders.
```
// my_awesome_split_board.conf
CONFIG_ZMK_SLEEP=y
```
</TabItem>
</Tabs>
## (Optional) Matrix Transform ## (Optional) Matrix Transform
Internally ZMK translates all row/column events into "key position" events to maintain a consistent model that works no matter what any possible GPIO matrix may look like for a certain keyboard. This is particularly helpful when: Internally ZMK translates all row/column events into "key position" events to maintain a consistent model that works no matter what any possible GPIO matrix may look like for a certain keyboard. This is particularly helpful when:
@ -279,15 +460,13 @@ For split keyboards, make sure to add left hand encoders to the left .overlay fi
</TabItem> </TabItem>
<TabItem value = "keymap"> <TabItem value = "keymap">
Add the following line to each layer of your keymap file to add default encoder behavior bindings: Add the following line to your keymap file to add default encoder behavior bindings:
``` ```
sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>; sensor-bindings = <&inc_dec_cp M_VOLU M_VOLD>;
``` ```
This should be placed after the regular key bindings but within the layer (see the [Default Keymap section](/docs/dev-guide-new-shield#default-keymap) above for an example of where). Add additional bindings as necessary to match the default number of encoders on your board. See the [Encoders](/docs/feature/encoders) and [Keymap](/docs/feature/keymaps) feature documentation for more details.
Add additional bindings as necessary to match the default number of encoders on your board. Details on the syntax can be found in the [Encoders](/docs/feature/encoders) and [Keymap](/docs/feature/keymaps) feature documentation.
</TabItem> </TabItem>
</Tabs> </Tabs>
@ -307,6 +486,11 @@ and then flash with:
west flash west flash
``` ```
:::note
Further testing your keyboard shield without altering the root keymap file can be done with the use of `-DZMK_CONFIG` in your `west build` command,
shown [here](dev-build-flash#building-from-zmk-config-folder)
:::
## Updating `build.yml` ## Updating `build.yml`
Before publishing your shield to the public via a PR, navigate to `build.yml` found in `.github/workflows` and add your shield to the appropriate list. An example edit to `build.yml` is shown below. Before publishing your shield to the public via a PR, navigate to `build.yml` found in `.github/workflows` and add your shield to the appropriate list. An example edit to `build.yml` is shown below.

2
docs/docs/dev-guide-usb-logging.md

@ -56,9 +56,11 @@ values={[
<TabItem value="linux"> <TabItem value="linux">
On Linux, this should be a device like `/dev/ttyACM0` and you can connect with `minicom` or `tio` as usual, e.g.: On Linux, this should be a device like `/dev/ttyACM0` and you can connect with `minicom` or `tio` as usual, e.g.:
``` ```
sudo tio /dev/ttyACM0 sudo tio /dev/ttyACM0
``` ```
</TabItem> </TabItem>
<TabItem value="win"> <TabItem value="win">

13
docs/docs/dev-setup.md

@ -168,6 +168,12 @@ Chocolatey is recommended and used for the following instructions. You can manua
choco install ninja gperf python git choco install ninja gperf python git
``` ```
It is recommended to install `dfu-util` to avoid any later confusion while flashing devices. You can do this by running this command with chocolatey:
```shell
choco install dfu-util
```
</TabItem> </TabItem>
<TabItem value="mac"> <TabItem value="mac">
@ -176,7 +182,7 @@ Chocolatey is recommended and used for the following instructions. You can manua
Homebrew is required to install the system dependencies. If you haven't done so, visit [Homebrew](https://brew.sh/) for instructions. Once installed, use it to install the base dependencies: Homebrew is required to install the system dependencies. If you haven't done so, visit [Homebrew](https://brew.sh/) for instructions. Once installed, use it to install the base dependencies:
``` ```
brew install cmake ninja python3 ccache dtc git wget brew install cmake ninja python3 ccache dtc git wget dfu-util
``` ```
</TabItem> </TabItem>
@ -184,7 +190,6 @@ brew install cmake ninja python3 ccache dtc git wget
This setup leverages the same [image which is used by the GitHub action](https://github.com/zmkfirmware/zephyr-west-action) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach is also the easiest to set up. No toolchain or dependencies are necessary when using Docker; the container image you'll be using already has the toolchain installed and set up to use. This setup leverages the same [image which is used by the GitHub action](https://github.com/zmkfirmware/zephyr-west-action) for local development. Beyond the benefits of [dev/prod parity](https://12factor.net/dev-prod-parity), this approach is also the easiest to set up. No toolchain or dependencies are necessary when using Docker; the container image you'll be using already has the toolchain installed and set up to use.
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system. 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system.
2. Install [VS Code](https://code.visualstudio.com/) 2. Install [VS Code](https://code.visualstudio.com/)
3. Install the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) 3. Install the [Remote - Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
@ -364,7 +369,6 @@ Since ZMK is built as a Zephyr™ application, the next step is
to use `west` to initialize and update your workspace. The ZMK to use `west` to initialize and update your workspace. The ZMK
Zephyr™ application is in the `app/` source directory: Zephyr™ application is in the `app/` source directory:
#### Step into the repository #### Step into the repository
<OsTabs> <OsTabs>
@ -415,7 +419,7 @@ Click `Reopen in Container` in order to reopen the VS Code with the running cont
The first time you do this on your machine, it will pull the docker image down from the registry and build the container. Subsequent launches are much faster! The first time you do this on your machine, it will pull the docker image down from the registry and build the container. Subsequent launches are much faster!
:::caution :::caution
All subsequent steps must be performed from the VS Code terminal _inside_ the container. All subsequent steps must be performed from the VS Code terminal _inside_ the container.
::: :::
</TabItem> </TabItem>
@ -449,7 +453,6 @@ If you're using Docker, you're done with setup! You must restart the container a
Once your container is restarted, proceed to [Building and Flashing](./dev-build.md). Once your container is restarted, proceed to [Building and Flashing](./dev-build.md).
::: :::
#### Export Zephyr™ Core #### Export Zephyr™ Core
```sh ```sh

9
docs/docs/dev-tests.md

@ -8,13 +8,14 @@ Running tests requires [native posix support](./dev-posix-board). Any folder und
containing `native_posix.keymap` will be selected when running `./run-test.sh all`. containing `native_posix.keymap` will be selected when running `./run-test.sh all`.
## Creating a New Test Set ## Creating a New Test Set
1. Copy the test set that most closely resembles the tests you will be creating. 1. Copy the test set that most closely resembles the tests you will be creating.
2. Rename the newly created test set to the behavior you're testing e.g, toggle-layer 2. Rename the newly created test set to the behavior you're testing e.g, toggle-layer
3. Modify `behavior_keymap.dtsi` to create a keymap using the behavior and related behaviors 3. Modify `behavior_keymap.dtsi` to create a keymap using the behavior and related behaviors
4. Modify `test_case/native_posix.keymap` for a simulated use case 4. Modify `test_case/native_posix.keymap` for a simulated use case
5. Modify `test_case/events.patterns` to collect relevant logs to the test 5. Modify `test_case/events.patterns` to collect relevant logs to the test
- See: [sed manual](https://www.gnu.org/software/sed/manual/sed.html) and - See: [sed manual](https://www.gnu.org/software/sed/manual/sed.html) and
[tutorial](https://www.digitalocean.com/community/tutorials/the-basics-of-using-the-sed-stream-editor-to-manipulate-text-in-linux) [tutorial](https://www.digitalocean.com/community/tutorials/the-basics-of-using-the-sed-stream-editor-to-manipulate-text-in-linux)
6. Modify `test_case/keycode_events.snapshot` for to include the expected output 6. Modify `test_case/keycode_events.snapshot` for to include the expected output
7. Rename the `test_case` folder to describe the test. 7. Rename the `test_case` folder to describe the test.
8. Repeat steps 4 to 7 for every test case 8. Repeat steps 4 to 7 for every test case

47
docs/docs/faq.md

@ -7,7 +7,8 @@ sidebar_label: FAQs
### Why Zephyr™? ### Why Zephyr™?
As a best-in-class RTOS, Zephyr™ brings many [benefits](https://www.zephyrproject.org/benefits) to ZMK, such as: As a best-in-class RTOS, Zephyr™ brings many [benefits](https://www.zephyrproject.org/benefits) to ZMK, such as:
- A *single* platform [supporting](https://docs.zephyrproject.org/latest/boards) many architectures, processors and boards.
- A _single_ platform [supporting](https://docs.zephyrproject.org/latest/boards) many architectures, processors and boards.
- Optimization for low-powered, small memory footprint devices. - Optimization for low-powered, small memory footprint devices.
- Powerful hardware abstraction and configuration using [DeviceTree](https://docs.zephyrproject.org/latest/guides/dts/index.html) and [Kconfig](https://docs.zephyrproject.org/latest/guides/kconfig/index.html). - Powerful hardware abstraction and configuration using [DeviceTree](https://docs.zephyrproject.org/latest/guides/dts/index.html) and [Kconfig](https://docs.zephyrproject.org/latest/guides/kconfig/index.html).
- A BLE stack that periodically obtains [qualification](https://docs.zephyrproject.org/latest/guides/bluetooth/bluetooth-qual.html) listings, making it easier for final products to obtain qualification from the Bluetooth® SIG. - A BLE stack that periodically obtains [qualification](https://docs.zephyrproject.org/latest/guides/bluetooth/bluetooth-qual.html) listings, making it easier for final products to obtain qualification from the Bluetooth® SIG.
@ -18,14 +19,14 @@ As a best-in-class RTOS, Zephyr™ brings many [benefits](https://www.zephyrproj
### Why yet another keyboard firmware? ### Why yet another keyboard firmware?
That’s an excellent question! There are already great keyboard firmwares available, but ZMK has some advantages: That’s an excellent question! There are already great keyboard firmwares available, but ZMK has some advantages:
- Zephyr™ - Zephyr™
- See [Why Zephyr™?](#why-zephyr) - See [Why Zephyr™?](#why-zephyr)
- Licensing - Licensing
- Just like other open source firmware, ZMK is all about the free and the sharing. However, some other projects use the GPL licence which prevents integration of libraries and drivers whose licenses are not GPL-compatible (such as some embedded BLE drivers). ZMK uses the permissive [MIT](https://github.com/zmkfirmware/zmk/blob/main/LICENSE) license which doesn’t have this limitation. - Just like other open source firmware, ZMK is all about the free and the sharing. However, some other projects use the GPL licence which prevents integration of libraries and drivers whose licenses are not GPL-compatible (such as some embedded BLE drivers). ZMK uses the permissive [MIT](https://github.com/zmkfirmware/zmk/blob/main/LICENSE) license which doesn’t have this limitation.
- Wireless First - Wireless First
- ZMK is designed for the future, and we believe the future is wireless. So power efficiency plays a critical role in every design decision, just like in Zephyr™. - ZMK is designed for the future, and we believe the future is wireless. So power efficiency plays a critical role in every design decision, just like in Zephyr™.
The ZMK contributors firmly believe that a keyboard firmware built on Zephyr™ will provide more long term benefits. The ZMK contributors firmly believe that a keyboard firmware built on Zephyr™ will provide more long term benefits.
@ -35,9 +36,9 @@ ZMK uses the MIT [license](https://github.com/zmkfirmware/zmk/blob/main/LICENSE)
### What hardware/platforms does ZMK support? ### What hardware/platforms does ZMK support?
ZMK has the potential to run on any platform supported by Zephyr™. However, it’s impractical for the ZMK contributors to test all possible hardware. ZMK has the potential to run on any platform supported by Zephyr™. However, it’s impractical for the ZMK contributors to test all possible hardware.
The Zephyr™ [documentation](https://docs.zephyrproject.org/latest/boards/index.html) describes which hardware is currently natively supported by the Zephyr™ platform. *Similar documentation covering which keyboards have been integrated into ZMK is currently being planned.* The Zephyr™ [documentation](https://docs.zephyrproject.org/latest/boards/index.html) describes which hardware is currently natively supported by the Zephyr™ platform. _Similar documentation covering which keyboards have been integrated into ZMK is currently being planned._
### Does ZMK compile for AVR? ### Does ZMK compile for AVR?
@ -45,54 +46,56 @@ Sorry, Zephyr™ only supports 32-bit and 64-bit platforms.
### How do I get started? ### How do I get started?
ZMK is still in its infancy, so there’s a learning curve involved. But if you’d like to try it out, please check out the development [documentation](/docs) and the other FAQs. Please keep in mind that the project team is still small, so our support capability is limited whilst we focus on development. But we’ll try our best! Interested developers are also very welcome to contribute! ZMK is still in its infancy, so there’s a learning curve involved. But if you’d like to try it out, please check out the development [documentation](/docs) and the other FAQs. Please keep in mind that the project team is still small, so our support capability is limited whilst we focus on development. But we’ll try our best! Interested developers are also very welcome to contribute!
### What is a “board”? ### What is a “board”?
In ZMK, a *board* defines the *PCB* that *includes the MCU*. In ZMK, a _board_ defines the _PCB_ that _includes the MCU_.
For keyboards, this is one of two options: For keyboards, this is one of two options:
- Complete keyboard PCBs that include the MCU (e.g. the Planck or Preonic). - Complete keyboard PCBs that include the MCU (e.g. the Planck or Preonic).
- Small MCU boards (e.g. the Proton-C or nice!nano) that expose pins and are designed to be combined with larger keyboard PCBs, or hand wired to switches to create the final keyboard. - Small MCU boards (e.g. the Proton-C or nice!nano) that expose pins and are designed to be combined with larger keyboard PCBs, or hand wired to switches to create the final keyboard.
### What is a “shield”? ### What is a “shield”?
In ZMK, a *shield* is a *PCB* or *hardwired set of components* that when combined with a MCU only [board](#what-is-a-board) like the Proton-C or nice!nano, results in a complete usable keyboard. Examples would be keyboard PCBs like the Kyria or Corne. The *shield* is usually the big PCB containing all the keys. In ZMK, a _shield_ is a _PCB_ or _hardwired set of components_ that when combined with a MCU only [board](#what-is-a-board) like the Proton-C or nice!nano, results in a complete usable keyboard. Examples would be keyboard PCBs like the Kyria or Corne. The _shield_ is usually the big PCB containing all the keys.
### Why *boards* and *shields*? Why not just “keyboard”? ### Why _boards_ and _shields_? Why not just “keyboard”?
If you haven't already done so, please read these FAQs first: If you haven't already done so, please read these FAQs first:
- [What is a “board”?](#what-is-a-board) - [What is a “board”?](#what-is-a-board)
- [What is a "shield"?](#what-is-a-shield) - [What is a "shield"?](#what-is-a-shield)
When a keyboard accepts a small “PCB MCU module” (e.g. *Arduino Pro Micro*) for its “brains”, then it's important to conceptually separate the hardware into a [board](#what-is-a-board) PCB and a [shield](#what-is-a-shield) PCB. When a keyboard accepts a small “PCB MCU module” (e.g. _Arduino Pro Micro_) for its “brains”, then it's important to conceptually separate the hardware into a [board](#what-is-a-board) PCB and a [shield](#what-is-a-shield) PCB.
The [shield](#what-is-a-shield) is a brainless shell containing all the keys, RGB LEDs, encoders etc. It maps all of these features to a standard pin footprint, such as the Pro Micro pinout. The [shield](#what-is-a-shield) is a brainless shell containing all the keys, RGB LEDs, encoders etc. It maps all of these features to a standard pin footprint, such as the Pro Micro pinout.
To bring this brainless [shield](#what-is-a-shield) to life, you attach any MCU [board](#what-is-a-board) matching the footprint. For instance, the *nice!nano* is *pin-compatible* with the *Arduino Pro Micro*, so you can substitute either [board](#what-is-a-board) onto the [shield](#what-is-a-shield). But each [board](#what-is-a-board) comes with its own features (MCU, flash, BLE, etc.) which must also be handled. To bring this brainless [shield](#what-is-a-shield) to life, you attach any MCU [board](#what-is-a-board) matching the footprint. For instance, the _nice!nano_ is _pin-compatible_ with the _Arduino Pro Micro_, so you can substitute either [board](#what-is-a-board) onto the [shield](#what-is-a-shield). But each [board](#what-is-a-board) comes with its own features (MCU, flash, BLE, etc.) which must also be handled.
Therefore in ZMK, [board](#what-is-a-board) and [shield](#what-is-a-shield) are considered two different (but related) entities so that it’s easier to mix and match them. They are combined during a ZMK build. Therefore in ZMK, [board](#what-is-a-board) and [shield](#what-is-a-shield) are considered two different (but related) entities so that it’s easier to mix and match them. They are combined during a ZMK build.
Please note, many keyboards only have a single PCB which includes the “brains” (MCU) onboard. In ZMK, these have no [shield](#what-is-a-shield), only a [board](#what-is-a-board). Please note, many keyboards only have a single PCB which includes the “brains” (MCU) onboard. In ZMK, these have no [shield](#what-is-a-shield), only a [board](#what-is-a-board).
### What bootloader does ZMK use? ### What bootloader does ZMK use?
ZMK isn’t designed for any particular bootloader, and supports flashing different boards with different flash utilities (e.g. OpenOCD, nrfjprog, etc.). So if you have any difficulties, please let us know on [Discord](https://zmkfirmware.dev/community/discord/invite)! ZMK isn’t designed for any particular bootloader, and supports flashing different boards with different flash utilities (e.g. OpenOCD, nrfjprog, etc.). So if you have any difficulties, please let us know on [Discord](https://zmkfirmware.dev/community/discord/invite)!
### Can I contribute? ### Can I contribute?
Of course! Please use the developer [documentation](/docs) to get started! Of course! Please use the developer [documentation](/docs) to get started!
### I have an idea! What should I do? ### I have an idea! What should I do?
Please join us on [Discord](https://zmkfirmware.dev/community/discord/invite) and discuss it with us! Please join us on [Discord](https://zmkfirmware.dev/community/discord/invite) and discuss it with us!
### I want to add a new keyboard! What should I do? ### I want to add a new keyboard! What should I do?
The exact process for the management of all the possible hardware is still being finalized, but any developer looking to contribute new keyboard definitions should chat with us on [Discord](https://zmkfirmware.dev/community/discord/invite) to get started. The exact process for the management of all the possible hardware is still being finalized, but any developer looking to contribute new keyboard definitions should chat with us on [Discord](https://zmkfirmware.dev/community/discord/invite) to get started.
### Does ZMK have a Code of Conduct? ### Does ZMK have a Code of Conduct?
Yes, it does have a [Code of Conduct](https://github.com/zmkfirmware/zmk/blob/main/CODE_OF_CONDUCT.md)! Please give it a read! Yes, it does have a [Code of Conduct](https://github.com/zmkfirmware/zmk/blob/main/CODE_OF_CONDUCT.md)! Please give it a read!
### What does “ZMK” mean? ### What does “ZMK” mean?
@ -100,8 +103,8 @@ ZMK was originally coined as a quasi-acronym of “Zephyr Mechanical Keyboard”
### Is ZMK related to TMK or QMK? ### Is ZMK related to TMK or QMK?
No. But inspired by, of course! No. But inspired by, of course!
### Who created ZMK? ### Who created ZMK?
ZMK was created by Pete Johanson. It is developed and maintained by the open source community. ZMK was created by Pete Johanson. It is developed and maintained by the open source community.

16
docs/docs/feature/underglow.md

@ -35,11 +35,17 @@ If your board or shield does not have RGB underglow configured, refer to [Adding
There are various Kconfig options used to configure the RGB underglow feature. These can all be set in the `.conf` file. There are various Kconfig options used to configure the RGB underglow feature. These can all be set in the `.conf` file.
| Option | Description | Default | | Option | Description | Default |
| ---------------------------- | ---------------------------------------------- | ------- | | ----------------------------- | ---------------------------------------------- | ------- |
| `ZMK_RGB_UNDERGLOW_HUE_STEP` | Hue step in degrees of 360 used by RGB actions | `10` | | `ZMK_RGB_UNDERGLOW_HUE_STEP` | Hue step in degrees of 360 used by RGB actions | 10 |
| `ZMK_RGB_UNDERGLOW_SAT_STEP` | Saturation step in percent used by RGB actions | `10` | | `ZMK_RGB_UNDERGLOW_SAT_STEP` | Saturation step in percent used by RGB actions | 10 |
| `ZMK_RGB_UNDERGLOW_BRT_STEP` | Brightness step in percent used by RGB actions | `10` | | `ZMK_RGB_UNDERGLOW_BRT_STEP` | Brightness step in percent used by RGB actions | 10 |
| `ZMK_RGB_UNDERGLOW_HUE_START` | Default hue 0-359 in degrees | 0 |
| `ZMK_RGB_UNDERGLOW_SAT_START` | Default saturation 0-100 in percent | 100 |
| `ZMK_RGB_UNDERGLOW_BRT_START` | Default brightness 0-100 in percent | 100 |
| `ZMK_RGB_UNDERGLOW_SPD_START` | Default effect speed 1-5 | 3 |
| `ZMK_RGB_UNDERGLOW_EFF_START` | Default effect integer from the effect enum | 0 |
| `ZMK_RGB_UNDERGLOW_ON_START` | Default on state | y |
## Adding RGB Underglow to a Board ## Adding RGB Underglow to a Board

4
docs/docs/hardware.md

@ -28,8 +28,10 @@ That being said, there are currently only a few specific [boards](/docs/faq#what
- [Lily58](https://github.com/kata0510/Lily58) (`lily58_left` and `lily58_right`) - [Lily58](https://github.com/kata0510/Lily58) (`lily58_left` and `lily58_right`)
- [Sofle](https://github.com/josefadamcik/SofleKeyboard) (`sofle_left` and `sofle_right`) - [Sofle](https://github.com/josefadamcik/SofleKeyboard) (`sofle_left` and `sofle_right`)
- [Splitreus62](https://github.com/Na-Cly/splitreus62) (`splitreus62_left` and `splitreus62_right`) - [Splitreus62](https://github.com/Na-Cly/splitreus62) (`splitreus62_left` and `splitreus62_right`)
- [Reviung41](https://github.com/gtips/reviung/tree/master/reviung41) (`reviung41`)
- [RoMac+ v4](https://www.littlekeyboards.com/products/romac) (`romac_plus`) - [RoMac+ v4](https://www.littlekeyboards.com/products/romac) (`romac_plus`)
- [RoMac v2](https://mechboards.co.uk/shop/kits/romac-macro-pad/) (`romac') - [RoMac v2](https://mechboards.co.uk/shop/kits/romac-macro-pad/) (`romac`)
- [Boardsource 3x4 Macro](https://boardsource.xyz/store/5ecc2008eee64242946c98c1) (`boardsource3x4`)
- [QAZ](https://www.cbkbd.com/product/qaz-keyboard-kit) (`qaz`) - [QAZ](https://www.cbkbd.com/product/qaz-keyboard-kit) (`qaz`)
## Other Hardware ## Other Hardware

63
docs/docs/intro.md

@ -2,42 +2,45 @@
id: intro id: intro
title: Introduction to ZMK title: Introduction to ZMK
sidebar_label: Introduction sidebar_label: Introduction
slug: /
--- ---
ZMK Firmware is an open source (MIT) keyboard ZMK Firmware is an open source (MIT) keyboard
firmware built on the [Zephyr™ Project](https://zephyrproject.org/) Real Time Operating System (RTOS). firmware built on the [Zephyr™ Project](https://zephyrproject.org/) Real Time Operating System (RTOS). ZMK's goal is to provide a modern, wireless, and powerful firmware free of licensing issues.
The goal is to provide a powerful, featureful keyboard firmware that is free
of licensing issues that prevent upstream BLE support as a first-class
feature.
## Features ## Features
At this point, ZMK is still missing many features. Currently, the working bits ZMK is currently missing some features found in other popular firmware. This table compares the features supported by ZMK, BlueMicro and QMK:
include:
| **Feature** | ZMK | BlueMicro | QMK |
- Wireless connectivity via BLE HID Over GATT (HOG) | ---------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
- USB connectivity | Low Latency BLE Support | ✅ | ✅ | |
- Low active power usage | Multi-Device BLE Support | ✅ | | |
- Split keyboard support | USB Connectivity | ✅ | | ✅ |
- [Keymaps and layers](behavior/layers) | User Configuration Repositories | ✅ | | |
- [Hold-tap](behavior/hold-tap) (which includes [mod-tap](behavior/mod-tap), [layer-tap](behavior/layers)) | Split Keyboard Support | ✅ | ✅ | ✅ |
- [Basic HID over USB](behavior/key-press) | [Keymaps and Layers](behavior/layers) | ✅ | ✅ | ✅ |
- [Basic consumer (media) keycodes](behavior/key-press#consumer-key-press) | [Hold-Tap](behavior/hold-tap) (which includes [Mod-Tap](behavior/mod-tap) and [Layer-Tap](behavior/layers/#layer-tap)) | ✅ | ✅ | ✅ |
- [Encoders](feature/encoders) | [Basic Keycodes](behavior/key-press) | ✅ | ✅ | ✅ |
- Basic [OLED display support](feature/displays) | [Basic consumer (Media) Keycodes](behavior/key-press#consumer-key-press) | ✅ | ✅ | ✅ |
- [RGB Underglow](feature/underglow) | [Encoders](feature/encoders)[^1] | ✅ | | ✅ |
| [OLED Display Support](feature/displays)[^2] | 🚧 | 🚧 | ✅ |
## Missing Features | [RGB Underglow](feature/underglow) | ✅ | ✅ | ✅ |
| One Shot Keys | 🚧 | ✅ | ✅ |
- One Shot Keys | Combo Keys | 🚧 | | ✅ |
- Combo keys | Macros | 🚧 | ✅ | ✅ |
- Macros | Mouse Keys | | ✅ | ✅ |
- Complete split support (encoders and RGB are not supported on the 'peripheral' side) | Low Active Power Usage | ✅ | | |
- Battery reporting | [Low Power Sleep States](https://github.com/zmkfirmware/zmk/pull/211) | 🚧 | ✅ | |
- Low power sleep states | [Low Power Mode (VCC Shutoff)](https://github.com/zmkfirmware/zmk/pull/242) | 🚧 | | |
- Low power mode (to toggle LEDs and screen off) | [Battery Reporting](https://github.com/zmkfirmware/zmk/issues/47) | 🚧 | ✅ | |
- Shell over BLE | Shell over BLE | | | |
| Realtime Keymap Updating | 💡 | | ✅ |
| AVR/8 Bit | | | ✅ |
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/latest/boards/index.html) | ✅ | | |
[^2]: Encoders are not currently supported on peripheral side splits.
[^1]: OLEDs are currently proof of concept in ZMK.
## Code Of Conduct ## Code Of Conduct

61
docs/docs/troubleshooting.md

@ -3,6 +3,7 @@ id: troubleshooting
title: Troubleshooting title: Troubleshooting
sidebar_title: Troubleshooting sidebar_title: Troubleshooting
--- ---
### Summary ### Summary
The following page provides suggestions for common errors that may occur during firmware compilation. If the information provided is insufficient to resolve the issue, feel free to seek out help from the [ZMK Discord](https://zmkfirmware.dev/community/discord/invite). The following page provides suggestions for common errors that may occur during firmware compilation. If the information provided is insufficient to resolve the issue, feel free to seek out help from the [ZMK Discord](https://zmkfirmware.dev/community/discord/invite).
@ -11,31 +12,30 @@ The following page provides suggestions for common errors that may occur during
Variations of the warnings shown below occur when flashing the `<firmware>.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig. Variations of the warnings shown below occur when flashing the `<firmware>.uf2` onto the microcontroller. This is because the microcontroller resets itself before the OS receives confirmation that the file transfer is complete. Errors like this are normal and can generally be ignored. Verification of a functional board can be done by attempting to pair your newly flashed keyboard to your computer via Bluetooth or plugging in a USB cable if `ZMK_USB` is enabled in your Kconfig.defconfig.
| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/windows.png) | | ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/windows.png) |
| :-------------------------------------------------------------------------------: | | :------------------------------------------------------------------------------: |
| An example of the file transfer error on Windows 10 | | An example of the file transfer error on Windows 10 |
| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/linux.png) |
| :-------------------------------------------------------------------------------: |
| An example of the file transfer error on Linux |
| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/mac.png) | | ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/linux.png) |
| :-------------------------------------------------------------------------------: | | :----------------------------------------------------------------------------: |
| An example of the file transfer error on MacOS | | An example of the file transfer error on Linux |
| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/mac.png) |
| :--------------------------------------------------------------------------: |
| An example of the file transfer error on MacOS |
### CMake Error ### CMake Error
``` ```
CMake Warning at C:/zmk/zephyr/subsys/usb/CMakeLists.txt:28 (message): CMake Warning at C:/zmk/zephyr/subsys/usb/CMakeLists.txt:28 (message):
CONFIG_USB_DEVICE_VID has default value 0x2FE3. CONFIG_USB_DEVICE_VID has default value 0x2FE3.
This value is only for testing and MUST be configured for USB products. This value is only for testing and MUST be configured for USB products.
CMake Warning at C:/zmk/zephyr/subsys/usb/CMakeLists.txt:34 (message): CMake Warning at C:/zmk/zephyr/subsys/usb/CMakeLists.txt:34 (message):
CONFIG_USB_DEVICE_PID has default value 0x100. CONFIG_USB_DEVICE_PID has default value 0x100.
This value is only for testing and MUST be configured for USB products. This value is only for testing and MUST be configured for USB products.
``` ```
@ -44,25 +44,24 @@ CMake Warnings shown above during `west build` are normal occurrences. They shou
On the other hand, an error along the lines of `CMake Error at (zmk directory)/zephyr/cmake/generic_toolchain.cmake:64 (include): include could not find load file:` during firmware compilation indicates that the Zephyr Environment Variables are not properly defined. On the other hand, an error along the lines of `CMake Error at (zmk directory)/zephyr/cmake/generic_toolchain.cmake:64 (include): include could not find load file:` during firmware compilation indicates that the Zephyr Environment Variables are not properly defined.
For more information, click [here](../docs/dev-setup#environment-variables). For more information, click [here](../docs/dev-setup#environment-variables).
### dtlib.DTError ### dtlib.DTError
An error along the lines of `dtlib.DTError: <board>.dts.pre.tmp:<line number>` during firmware compilation indicates an issue within the `<shield>.keymap` file. An error along the lines of `dtlib.DTError: <board>.dts.pre.tmp:<line number>` during firmware compilation indicates an issue within the `<shield>.keymap` file.
This can be verified by checking the file in question, found in `mkdir/app/build`. This can be verified by checking the file in question, found in `mkdir/app/build`.
| ![Example Error Screen](../docs/assets/troubleshooting/keymaps/errorscreen.png) | | ![Example Error Screen](../docs/assets/troubleshooting/keymaps/errorscreen.png) |
| :-------------------------------------------------------------------------------: | | :----------------------------------------------------------------------------------------------------------------: |
| An example of the dtlib.DTError when compiling an iris with the nice!nano while the keymap is not properly defined | | An example of the dtlib.DTError when compiling an iris with the nice!nano while the keymap is not properly defined |
After opening the `<board>.dts.pre.tmp:<line number>` and scrolling down to the referenced line, one can locate errors within their shield's keymap by checking if the referenced keycodes were properly converted into the correct [USB HID Usage ID](https://www.usb.org/document-library/hid-usage-tables-12). After opening the `<board>.dts.pre.tmp:<line number>` and scrolling down to the referenced line, one can locate errors within their shield's keymap by checking if the referenced keycodes were properly converted into the correct [USB HID Usage ID](https://www.usb.org/document-library/hid-usage-tables-12).
| ![Unhealthy Keymap Temp](../docs/assets/troubleshooting/keymaps/unhealthyEDIT.png) | | ![Unhealthy Keymap Temp](../docs/assets/troubleshooting/keymaps/unhealthyEDIT.png) |
| :-------------------------------------------------------------------------------: | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| An incorrectly defined keymap unable to compile. As shown in red, `&kp SPAC` is not a valid reference to the [USB HID Usage ID](https://www.usb.org/document-library/hid-usage-tables-12) used for "Keyboard Spacebar" | | An incorrectly defined keymap unable to compile. As shown in red, `&kp SPAC` is not a valid reference to the [USB HID Usage ID](https://www.usb.org/document-library/hid-usage-tables-12) used for "Keyboard Spacebar" |
| ![Healthy Keymap Temp](../docs/assets/troubleshooting/keymaps/healthyEDIT.png) | | ![Healthy Keymap Temp](../docs/assets/troubleshooting/keymaps/healthyEDIT.png) |
| :-------------------------------------------------------------------------------: | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| A properly defined keymap with successful compilation. As shown in red, the corrected keycode (`&kp SPC`) references the proper Usage ID defined in the [USB HID Usage Tables](https://www.usb.org/document-library/hid-usage-tables-12)| | A properly defined keymap with successful compilation. As shown in red, the corrected keycode (`&kp SPC`) references the proper Usage ID defined in the [USB HID Usage Tables](https://www.usb.org/document-library/hid-usage-tables-12) |
### Split Keyboard Halves Unable to Pair ### Split Keyboard Halves Unable to Pair
@ -74,9 +73,19 @@ Since then, a much simpler procedure of performing a bluetooth reset for split k
1. Log into Github and download the "settings clear" UF2 image from the [latest build in Github Actions](https://github.com/zmkfirmware/zmk/actions?query=workflow%3ABuild+branch%3Amain) 1. Log into Github and download the "settings clear" UF2 image from the [latest build in Github Actions](https://github.com/zmkfirmware/zmk/actions?query=workflow%3ABuild+branch%3Amain)
1. Put each half of the split keyboard into bootloader mode 1. Put each half of the split keyboard into bootloader mode
1. Flash one of the halves of the split with the "settings clear" UF2 image from step 1. Immediately after flashing "settings clear" to the chosen half, immediately put it into bootloader mode 1. Flash one of the halves of the split with the "settings clear" UF2 image from step 1. Immediately after flashing "settings clear" to the chosen half, immediately put it into bootloader mode
to avoid accidental bonding between the halves. to avoid accidental bonding between the halves.
1. Repeat step 3 with the other half of the split keyboard 1. Repeat step 3 with the other half of the split keyboard
1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half) 1. Flash the actual image for each half of the split keyboard (e.g `my_board_left.uf2` to the left half, `my_board_right.uf2` to the right half)
After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins After completing these steps, pair the halves of the split keyboard together by resetting them at the same time. Most commonly, this is done by grounding the reset pins
for each of your keyboard's microcontrollers or pressing the reset buttons at the same time. for each of your keyboard's microcontrollers or pressing the reset buttons at the same time.
### Connectivity Issues
Some users may experience a poor connection between the keyboard and the host. This might be due to poor quality BLE hardware, a metal enclosure on the keyboard or host, or the distance between them. Increasing the transmit power of the keyboard's BLE radio may reduce the severity of this problem. To do this, set the `CONFIG_BT_CTLR_TX_PWR_PLUS_8` configuration value in the `.conf` file of your user config directory as such:
```
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
```
For the `nRF52840`, the value `PLUS_8` can be set to any multiple of four between `MINUS_20` and `PLUS_8`. The default value for this config is `0`, but if you are having connection issues it is recommended to set it to `PLUS_8` because the power consumption difference is negligible. For more information on changing the transmit power of your BLE device, please refer to [the Zephyr docs.](https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_BT_CTLR_TX_PWR_PLUS_8.html)

10
docs/docs/user-setup.md

@ -63,6 +63,7 @@ defaultValue="curl"
values={[ values={[
{label: 'Using curl', value: 'curl'}, {label: 'Using curl', value: 'curl'},
{label: 'Using wget', value: 'wget'}, {label: 'Using wget', value: 'wget'},
{label: 'Using PowerShell', value: 'PowerShell'},
]}> ]}>
<TabItem value="curl"> <TabItem value="curl">
@ -74,7 +75,14 @@ bash -c "$(curl -fsSL https://zmkfirmware.dev/setup.sh)"
<TabItem value="wget"> <TabItem value="wget">
``` ```
bash -c "$(wget https://zmkfirmware.dev/setup.sh -O -)" bash -c "$(wget https://zmkfirmware.dev/setup.sh -O -)" '' --wget
```
</TabItem>
<TabItem value="PowerShell">
```
iex ((New-Object System.Net.WebClient).DownloadString('https://zmkfirmware.dev/setup.ps1'))"
``` ```
</TabItem> </TabItem>

3
docs/docusaurus.config.js

@ -89,7 +89,7 @@ module.exports = {
], ],
}, },
], ],
copyright: `Copyright © ${new Date().getFullYear()} ZMK Project Contributors, Built with Docusaurus.`, copyright: `Copyright © ${new Date().getFullYear()} ZMK Project Contributors. <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/80x15.png" /></a>`,
}, },
algolia: { algolia: {
apiKey: "75325855fc90356828fe212d38e5ca34", apiKey: "75325855fc90356828fe212d38e5ca34",
@ -102,7 +102,6 @@ module.exports = {
{ {
docs: { docs: {
// It is recommended to set document id as docs home page (`docs/` path). // It is recommended to set document id as docs home page (`docs/` path).
homePageId: "intro",
sidebarPath: require.resolve("./sidebars.js"), sidebarPath: require.resolve("./sidebars.js"),
// Please change this to your repo. // Please change this to your repo.
editUrl: "https://github.com/zmkfirmware/zmk/edit/main/docs/", editUrl: "https://github.com/zmkfirmware/zmk/edit/main/docs/",

24610
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

15
docs/package.json

@ -5,12 +5,14 @@
"scripts": { "scripts": {
"start": "docusaurus start", "start": "docusaurus start",
"build": "docusaurus build", "build": "docusaurus build",
"serve": "docusaurus serve",
"swizzle": "docusaurus swizzle", "swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy" "deploy": "docusaurus deploy",
"clear": "docusaurus clear"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "^2.0.0-alpha.61", "@docusaurus/core": "^2.0.0-alpha.66",
"@docusaurus/preset-classic": "^2.0.0-alpha.61", "@docusaurus/preset-classic": "^2.0.0-alpha.66",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"react": "^16.8.4", "react": "^16.8.4",
"react-dom": "^16.8.4" "react-dom": "^16.8.4"
@ -26,5 +28,12 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"eslint": "^7.12.0",
"eslint-config-prettier": "^6.14.0",
"eslint-plugin-mdx": "^1.8.2",
"eslint-plugin-react": "^7.21.5",
"prettier": "2.1.2"
} }
} }

7
docs/sidebars.js

@ -6,7 +6,7 @@ module.exports = {
"faq", "faq",
"user-setup", "user-setup",
"customization", "customization",
"troubleshooting" "troubleshooting",
], ],
Features: [ Features: [
"feature/keymaps", "feature/keymaps",
@ -23,6 +23,7 @@ module.exports = {
"behavior/reset", "behavior/reset",
"behavior/bluetooth", "behavior/bluetooth",
"behavior/lighting", "behavior/lighting",
"behavior/power",
], ],
Development: [ Development: [
"dev-clean-room", "dev-clean-room",
@ -32,8 +33,6 @@ module.exports = {
"dev-posix-board", "dev-posix-board",
"dev-tests", "dev-tests",
], ],
"Dev Guides": [ "Dev Guides": ["dev-guide-new-shield", "dev-guide-usb-logging"],
"dev-guide-new-shield",
"dev-guide-usb-logging"],
}, },
}; };

20
docs/src/pages/index.js

@ -5,6 +5,7 @@ import Link from "@docusaurus/Link";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl"; import useBaseUrl from "@docusaurus/useBaseUrl";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
import PropTypes from "prop-types";
const features = [ const features = [
{ {
@ -12,7 +13,8 @@ const features = [
imageUrl: "img/undraw_zephyr.svg", imageUrl: "img/undraw_zephyr.svg",
description: ( description: (
<> <>
With a wide range of architecture support, ZMK is ready for many existing keyboards. With a wide range of architecture support, ZMK is ready for many
existing keyboards.
</> </>
), ),
}, },
@ -20,19 +22,13 @@ const features = [
title: <>Permissive Licensing</>, title: <>Permissive Licensing</>,
imageUrl: "img/undraw_open_source.svg", imageUrl: "img/undraw_open_source.svg",
description: ( description: (
<> <>MIT licensed to remove any future limitations in innovation.</>
MIT licensed to remove any future limitations in innovation.
</>
), ),
}, },
{ {
title: <>Wireless First</>, title: <>Wireless First</>,
imageUrl: "img/undraw_wireless.svg", imageUrl: "img/undraw_wireless.svg",
description: ( description: <>Designed for the future, including wireless support.</>,
<>
Designed for the future, including wireless support.
</>
),
}, },
]; ];
@ -51,6 +47,12 @@ function Feature({ imageUrl, title, description }) {
); );
} }
Feature.propTypes = {
imageUrl: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
};
function Home() { function Home() {
const context = useDocusaurusContext(); const context = useDocusaurusContext();
const { siteConfig = {} } = context; const { siteConfig = {} } = context;

6
docs/static/setup.ps1 vendored

@ -91,9 +91,9 @@ Write-Host "Keyboard Shield Selection:"
$prompt = "Pick a keyboard" $prompt = "Pick a keyboard"
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos. # TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
$options = "Kyria", "Lily58", "Corne", "Splitreus62", "Sofle", "Iris", "RoMac", "makerdiary M60", "Microdox" $options = "Kyria", "Lily58", "Corne", "Splitreus62", "Sofle", "Iris", "Reviung41", "RoMac", "RoMac+", "makerdiary M60", "Microdox", "TG4X", "QAZ", "NIBBLE"
$names = "kyria", "lily58", "corne", "splitreus62", "sofle", "iris", "romac", "m60", "microdox" $names = "kyria", "lily58", "corne", "splitreus62", "sofle", "iris", "reviung41", "romac", "romac_plus", "m60", "microdox", "tg4x", "qaz", "nibble"
$splits = "y", "y", "y", "y", "y", "y", "n", "n", "y" $splits = "y", "y", "y", "y", "y", "y", "n", "n", "n", "n", "y", "n", "n", "n"
$choice = Get-Choice-From-Options -Options $options -Prompt $prompt $choice = Get-Choice-From-Options -Options $options -Prompt $prompt
$shield_title = $($options[$choice]) $shield_title = $($options[$choice])

57
docs/static/setup.sh vendored

@ -9,15 +9,25 @@ set -e
check_exists() { check_exists() {
command_to_run=$1 command_to_run=$1
error_message=$2 error_message=$2
local __resultvar=$3
if ! eval "$command_to_run" &> /dev/null; then if ! eval "$command_to_run" &> /dev/null; then
printf "%s\n" "$error_message" if [[ "$__resultvar" != "" ]]; then
exit 1 eval $__resultvar="'false'"
else
printf "%s\n" "$error_message"
exit 1
fi
else
if [[ "$__resultvar" != "" ]]; then
eval $__resultvar="'true'"
fi
fi fi
} }
check_exists "command -v git" "git is not installed, and is required for this script!" check_exists "command -v git" "git is not installed, and is required for this script!"
check_exists "command -v curl" "curl is not installed, and is required for this script!" check_exists "command -v curl" "curl is not installed, and is required for this script!" curl_exists
check_exists "command -v wget" "wget is not installed, and is required for this script!" wget_exists
check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'" check_exists "git config user.name" "Git username not set!\nRun: git config --global user.name 'My Name'"
check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'" check_exists "git config user.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
@ -29,6 +39,30 @@ if [ ! -w `pwd` ]; then
exit 1 exit 1
fi fi
# Parse all commandline options
while [[ "$#" -gt 0 ]]; do
case $1 in
-w|--wget) force_wget="true"; break;;
*) echo "Unknown parameter: $1"; exit 1;;
esac
shift
done
if [[ $curl_exists == "true" && $wget_exists == "true" ]]; then
if [[ $force_wget == "true" ]]; then
download_command="wget "
else
download_command="curl -O "
fi
elif [[ $curl_exists == "true" ]]; then
download_command="curl -O "
elif [[ $wget_exists == "true" ]]; then
download_command="wget "
else
echo 'Neither curl nor wget are installed. One of the two is required for this script!'
exit 1
fi
repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git" repo_path="https://github.com/zmkfirmware/zmk-config-split-template.git"
title="ZMK Config Setup:" title="ZMK Config Setup:"
@ -58,7 +92,7 @@ echo ""
echo "Keyboard Shield Selection:" echo "Keyboard Shield Selection:"
prompt="Pick an keyboard:" prompt="Pick an keyboard:"
options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "RoMac" "makerdiary M60" "Microdox") options=("Kyria" "Lily58" "Corne" "Splitreus62" "Sofle" "Iris" "Reviung41" "RoMac" "RoMac+" "makerdiary M60" "Microdox" "TG4X" "QAZ")
PS3="$prompt " PS3="$prompt "
# TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos. # TODO: Add support for "Other" and linking to docs on adding custom shields in user config repos.
@ -73,9 +107,14 @@ select opt in "${options[@]}" "Quit"; do
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;; 4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;; 5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
6 ) shield_title="Iris" shield="iris"; split="y"; break;; 6 ) shield_title="Iris" shield="iris"; split="y"; break;;
7 ) shield_title="RoMac" shield="romac"; split="n"; break;; 7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
8 ) shield_title="M60" shield="m60"; split="n"; break;; 8 ) shield_title="RoMac" shield="romac"; split="n"; break;;
9 ) shield_title="Microdox" shield="microdox"; split="y"; break;; 9 ) shield_title="RoMac+" shield="romac_plus"; split="n"; break;;
10 ) shield_title="M60" shield="m60"; split="n"; break;;
11 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
12 ) shield_title="TG4X" shield="tg4x"; split="n"; break;;
13 ) shield_title="QAZ" shield="qaz"; split="n"; break;;
14 ) shield_title="NIBBLE" shield="nibble"; split="n"; break;;
# Add link to docs on adding your own custom shield in your ZMK config! # Add link to docs on adding your own custom shield in your ZMK config!
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;; # $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
@ -133,10 +172,10 @@ cd ${repo_name}
pushd config pushd config
curl -O "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf" $download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.conf"
if [ "$copy_keymap" == "yes" ]; then if [ "$copy_keymap" == "yes" ]; then
curl -O "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap" $download_command "https://raw.githubusercontent.com/zmkfirmware/zmk/main/app/boards/shields/${shield}/${shield}.keymap"
fi fi
popd popd

Loading…
Cancel
Save