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

4
.devcontainer/.bashrc

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
export LS_OPTIONS='-F --color=auto'
eval "`dircolors`"
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
source "$WORKSPACE_DIR/zephyr/zephyr-env.sh"
fi

5
.devcontainer/devcontainer.json

@ -3,6 +3,9 @@ @@ -3,6 +3,9 @@
"dockerFile": "Dockerfile",
"extensions": ["ms-vscode.cpptools"],
"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: @@ -10,6 +10,7 @@ jobs:
matrix:
board: [nice_nano, bluemicro840_v1, nrfmicro_13]
shield:
- boardsource3x4
- corne_left
- corne_right
- kyria_left
@ -18,11 +19,13 @@ jobs: @@ -18,11 +19,13 @@ jobs:
- lily58_right
- iris_left
- iris_right
- reviung41
- romac
- romac_plus
- settings_reset
- quefrency_left
- quefrency_right
- nibble
include:
- board: proton_c
shield: clueboard_california

1
.gitignore vendored

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

2
README.md

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
[![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
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/

2
app/CMakeLists.txt

@ -46,6 +46,7 @@ if (NOT CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) @@ -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_none.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)
endif()
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) @@ -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(app PRIVATE src/endpoints.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)
zephyr_cc_option(-Wfatal-errors)

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

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

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

@ -29,6 +29,18 @@ @@ -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 {

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

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

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

@ -34,6 +34,18 @@ @@ -34,6 +34,18 @@
label = "EXT_POWER";
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 {

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

@ -35,6 +35,9 @@ if BOARD_NRFMICRO_13 @@ -35,6 +35,9 @@ if BOARD_NRFMICRO_13
config BOARD_NRFMICRO_CHARGER
default y
config ZMK_BATTERY_VOLTAGE_DIVIDER
default y
endif # 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 @@ @@ -31,6 +31,18 @@
label = "EXT_POWER";
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 {

14
app/boards/shields/Kconfig.defconfig

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

3
app/boards/shields/lily58/lily58.keymap

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#include <dt-bindings/zmk/ext_power.h>
/ {
keymap {
@ -41,7 +42,7 @@ @@ -41,7 +42,7 @@
&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 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
>;

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

@ -0,0 +1,13 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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) @@ -5,8 +5,10 @@ if(CONFIG_ZMK_KSCAN_GPIO_DRIVER)
zephyr_library_sources(
kscan_gpio_matrix.c
kscan_gpio_direct.c
kscan_gpio_demux.c
)
zephyr_library_sources_ifdef(CONFIG_EC11 ec11.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()

6
app/drivers/zephyr/Kconfig

@ -21,6 +21,12 @@ config ZMK_KSCAN_INIT_PRIORITY @@ -21,6 +21,12 @@ config ZMK_KSCAN_INIT_PRIORITY
help
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
bool "EC11 Incremental Encoder Sensor"
depends on GPIO

215
app/drivers/zephyr/battery_voltage_divider.c

@ -0,0 +1,215 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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) */

17
app/drivers/zephyr/kscan_gpio_matrix.c

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

1
app/dts/behaviors.dtsi

@ -9,3 +9,4 @@ @@ -9,3 +9,4 @@
#include <behaviors/sensor_rotate_key_press.dtsi>
#include <behaviors/rgb_underglow.dtsi>
#include <behaviors/bluetooth.dtsi>
#include <behaviors/ext_power.dtsi>

9
app/dts/behaviors/ext_power.dtsi

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

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

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

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

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

2
app/src/behaviors/behavior_bt.c

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

57
app/src/behaviors/behavior_ext_power.c

@ -0,0 +1,57 @@ @@ -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);

146
app/src/ble.c

@ -45,10 +45,29 @@ static u8_t passkey_digit = 0; @@ -45,10 +45,29 @@ static u8_t passkey_digit = 0;
#define PROFILE_COUNT CONFIG_BT_MAX_PAIRED
#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 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[] = {
#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_UUID16_SOME,
#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) { @@ -92,29 +111,101 @@ void set_profile_address(u8_t index, const bt_addr_le_t *addr) {
raise_profile_changed_event();
}
int zmk_ble_adv_pause() {
int err = bt_le_adv_stop();
if (err) {
LOG_ERR("Failed to stop advertising (err %d)", err);
return err;
bool active_profile_is_connected() {
struct bt_conn *conn;
bt_addr_le_t *addr = zmk_ble_active_profile_addr();
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
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() {
LOG_DBG("active_profile %d, directed? %s", active_profile,
active_profile_is_open() ? "no" : "yes");
return true;
}
int err = bt_le_adv_start(BT_LE_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;
#define CHECKED_ADV_STOP() \
err = bt_le_adv_stop(); \
advertising_status = ZMK_ADV_NONE; \
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;
};
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() {
LOG_DBG("");
@ -124,6 +215,8 @@ int zmk_ble_clear_bonds() { @@ -124,6 +215,8 @@ int zmk_ble_clear_bonds() {
set_profile_address(active_profile, BT_ADDR_LE_ANY);
}
update_advertising();
return 0;
};
@ -134,9 +227,13 @@ int zmk_ble_prof_select(u8_t index) { @@ -134,9 +227,13 @@ int zmk_ble_prof_select(u8_t 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();
return 0;
};
int zmk_ble_prof_next() {
@ -234,8 +331,11 @@ static void connected(struct bt_conn *conn, u8_t err) { @@ -234,8 +331,11 @@ static void connected(struct bt_conn *conn, u8_t err) {
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
advertising_status = ZMK_ADV_NONE;
if (err) {
LOG_WRN("Failed to connect to %s (%u)", log_strdup(addr), err);
update_advertising();
return;
}
@ -250,6 +350,8 @@ static void connected(struct bt_conn *conn, u8_t err) { @@ -250,6 +350,8 @@ static void connected(struct bt_conn *conn, u8_t err) {
if (bt_conn_set_security(conn, BT_SECURITY_L2)) {
LOG_ERR("Failed to set security");
}
update_advertising();
}
static void disconnected(struct bt_conn *conn, u8_t reason) {
@ -259,14 +361,9 @@ 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);
#if IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL)
// if (bt_addr_le_cmp(&peripheral_addr, BT_ADDR_LE_ANY) && bt_addr_le_cmp(&peripheral_addr,
// bt_conn_get_dst(conn))) {
// zmk_ble_adv_resume();
// }
#else
// zmk_ble_adv_resume();
#endif
// We need to do this in a work callback, otherwise the advertising update will still see the
// connection for a profile as active, and not start advertising yet.
k_work_submit(&update_advertising_work);
}
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) { @@ -361,6 +458,7 @@ static void auth_pairing_complete(struct bt_conn *conn, bool bonded) {
#endif /* !IS_ENABLED(CONFIG_ZMK_SPLIT_BLE_ROLE_PERIPHERAL) */
set_profile_address(active_profile, dst);
update_advertising();
};
static struct bt_conn_auth_cb zmk_ble_auth_cb_display = {
@ -383,7 +481,7 @@ static void zmk_ble_ready(int err) { @@ -383,7 +481,7 @@ static void zmk_ble_ready(int err) {
return;
}
zmk_ble_adv_resume();
update_advertising();
}
static int zmk_ble_init(struct device *_arg) {

2
app/src/events/ble_active_profile_changed.c

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

10
app/src/hog.c

@ -164,8 +164,10 @@ int zmk_hog_send_keypad_report(struct zmk_hid_keypad_report_body *report) { @@ -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");
return bt_gatt_notify(conn, &hog_svc.attrs[5], report,
sizeof(struct zmk_hid_keypad_report_body));
int err =
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) {
@ -174,6 +176,8 @@ 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 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));
bt_conn_unref(conn);
return err;
};

2
app/src/kscan_mock.c

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

95
app/src/rgb_underglow.c

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include <device.h>
#include <init.h>
#include <kernel.h>
#include <settings/settings.h>
#include <math.h>
#include <stdlib.h>
@ -14,7 +15,6 @@ @@ -14,7 +15,6 @@
#include <logging/log.h>
#include <drivers/led_strip.h>
#include <device.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -45,12 +45,36 @@ struct rgb_underglow_state { @@ -45,12 +45,36 @@ struct rgb_underglow_state {
bool on;
};
struct rgb_underglow_state state;
struct device *led_strip;
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) {
double r, g, b;
@ -100,6 +124,14 @@ static struct led_rgb hsb_to_rgb(struct led_hsb hsb) { @@ -100,6 +124,14 @@ static struct led_rgb hsb_to_rgb(struct led_hsb hsb) {
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() {
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
int hue = state.hue;
@ -182,6 +214,14 @@ static void zmk_rgb_underglow_tick(struct k_work *work) { @@ -182,6 +214,14 @@ static void zmk_rgb_underglow_tick(struct k_work *work) {
K_WORK_DEFINE(underglow_work, zmk_rgb_underglow_tick);
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);
}
@ -197,20 +237,32 @@ static int zmk_rgb_underglow_init(struct device *_arg) { @@ -197,20 +237,32 @@ static int zmk_rgb_underglow_init(struct device *_arg) {
}
state = (struct rgb_underglow_state){
hue : 0,
saturation : 100,
brightness : 100,
animation_speed : 3,
current_effect : 0,
hue : CONFIG_ZMK_RGB_UNDERGLOW_HUE_START,
saturation : CONFIG_ZMK_RGB_UNDERGLOW_SAT_START,
brightness : CONFIG_ZMK_RGB_UNDERGLOW_BRT_START,
animation_speed : CONFIG_ZMK_RGB_UNDERGLOW_SPD_START,
current_effect : CONFIG_ZMK_RGB_UNDERGLOW_EFF_START,
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));
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) {
if (!led_strip)
return -ENODEV;
@ -228,7 +280,7 @@ int zmk_rgb_underglow_cycle_effect(int direction) { @@ -228,7 +280,7 @@ int zmk_rgb_underglow_cycle_effect(int direction) {
state.animation_step = 0;
return 0;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_toggle() {
@ -241,17 +293,12 @@ int zmk_rgb_underglow_toggle() { @@ -241,17 +293,12 @@ int zmk_rgb_underglow_toggle() {
state.animation_step = 0;
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
} else {
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);
zmk_rgb_underglow_off();
k_timer_stop(&underglow_tick);
}
return 0;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_change_hue(int direction) {
@ -259,17 +306,15 @@ int zmk_rgb_underglow_change_hue(int direction) { @@ -259,17 +306,15 @@ int zmk_rgb_underglow_change_hue(int direction) {
return -ENODEV;
if (state.hue == 0 && direction < 0) {
state.hue = 350;
state.hue = 360 - CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
return 0;
}
state.hue += direction * CONFIG_ZMK_RGB_UNDERGLOW_HUE_STEP;
if (state.hue > 350) {
state.hue = 0;
}
state.hue = state.hue % 360;
return 0;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_change_sat(int direction) {
@ -286,7 +331,7 @@ int zmk_rgb_underglow_change_sat(int direction) { @@ -286,7 +331,7 @@ int zmk_rgb_underglow_change_sat(int direction) {
state.saturation = 100;
}
return 0;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_change_brt(int direction) {
@ -303,7 +348,7 @@ int zmk_rgb_underglow_change_brt(int direction) { @@ -303,7 +348,7 @@ int zmk_rgb_underglow_change_brt(int direction) {
state.brightness = 100;
}
return 0;
return zmk_rgb_underglow_save_state();
}
int zmk_rgb_underglow_change_spd(int direction) {
@ -320,7 +365,7 @@ int zmk_rgb_underglow_change_spd(int direction) { @@ -320,7 +365,7 @@ int zmk_rgb_underglow_change_spd(int direction) {
state.animation_speed = 5;
}
return 0;
return zmk_rgb_underglow_save_state();
}
SYS_INIT(zmk_rgb_underglow_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

8
app/src/settings.c

@ -0,0 +1,8 @@ @@ -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 @@ @@ -0,0 +1,2 @@
node_modules
build

29
docs/.eslintrc.js

@ -0,0 +1,29 @@ @@ -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 @@ @@ -0,0 +1,3 @@
node_modules
build
.docusaurus

3
docs/.prettierrc.js

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

5
docs/README.md

@ -2,6 +2,10 @@ @@ -2,6 +2,10 @@
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
```
@ -23,4 +27,3 @@ $ npm build @@ -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.

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

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

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

@ -4,12 +4,13 @@ sidebar_label: Hold-Tap @@ -4,12 +4,13 @@ sidebar_label: Hold-Tap
---
## 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
The `tapping_term_ms` parameter decides between a 'tap' and a 'hold'.
![Simple 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 @@ -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)
### 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
A code example which configures a mod-tap setting that works with homerow mods:
```
@ -63,4 +65,5 @@ If this config does not work for you, try the flavor "tap-preferred" and a short @@ -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.
#### 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`.

2
docs/docs/behavior/layers.md

@ -46,6 +46,7 @@ Example: @@ -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).
### Behavior Binding
- Reference: `&lt`
- Parameter: The layer number to enable when held, e.g. `1`
- Parameter: The keycode to send when tapped, e.g. `A`
@ -56,7 +57,6 @@ Example: @@ -56,7 +57,6 @@ Example:
&lt LOWER SPC
```
## Toggle Layer
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: @@ -40,4 +40,3 @@ You can configure a different tapping term in your keymap:
}
}
```

64
docs/docs/behavior/power.md

@ -0,0 +1,64 @@ @@ -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
```

1
docs/docs/bond-reset.md

@ -17,7 +17,6 @@ list, you will need to clear the bonds. @@ -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.
### Kyria
![Kyria bond-reset combo](assets/bond-clearing/kyria.jpg)

28
docs/docs/customization.md

@ -1,10 +1,22 @@ @@ -1,10 +1,22 @@
---
id: customization
title: Customizing ZMK
title: Customizing ZMK/`zmk-config` folders
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.
[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
@ -26,8 +38,22 @@ GitHub Actions job to build your firmware which you can download once it complet @@ -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.
:::
## 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
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.
More troubleshooting information for split keyboards can be found [here](troubleshooting#split-keyboard-halves-unable-to-pair).

7
docs/docs/dev-build.md

@ -62,11 +62,13 @@ west build -b planck_rev6 @@ -62,11 +62,13 @@ west build -b planck_rev6
```
### 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:
```sh
west build -p -b proton_c -- -DSHIELD=kyria_left
```
### Building For Split Keyboards
:::note
@ -78,10 +80,13 @@ By default, the `build` command outputs a single .uf2 file named `zmk.uf2` so bu @@ -78,10 +80,13 @@ 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
```
and then building right into `build/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.
### Building from `zmk-config` Folder
@ -89,14 +94,12 @@ This produces `left` and `right` subfolders under the `build` directory and two @@ -89,14 +94,12 @@ This produces `left` and `right` subfolders under the `build` directory and two
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.**
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
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: @@ -13,7 +13,7 @@ The high level steps are:
- Create a new shield directory.
- 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.
- 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.
@ -21,6 +21,10 @@ The high level steps are: @@ -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.
:::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
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 @@ -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.
**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
The `Kconfig.defconfig` file is where overrides for various configuration settings
@ -63,13 +77,40 @@ config ZMK_KEYBOARD_NAME @@ -63,13 +77,40 @@ config ZMK_KEYBOARD_NAME
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)
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:
```
@ -98,6 +139,146 @@ 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
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 @@ -279,15 +460,13 @@ For split keyboards, make sure to add left hand encoders to the left .overlay fi
</TabItem>
<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>;
```
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. Details on the syntax can be found in the [Encoders](/docs/feature/encoders) and [Keymap](/docs/feature/keymaps) feature documentation.
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.
</TabItem>
</Tabs>
@ -307,6 +486,11 @@ and then flash with: @@ -307,6 +486,11 @@ and then flash with:
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`
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={[ @@ -56,9 +56,11 @@ values={[
<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.:
```
sudo tio /dev/ttyACM0
```
</TabItem>
<TabItem value="win">

11
docs/docs/dev-setup.md

@ -168,6 +168,12 @@ Chocolatey is recommended and used for the following instructions. You can manua @@ -168,6 +168,12 @@ Chocolatey is recommended and used for the following instructions. You can manua
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 value="mac">
@ -176,7 +182,7 @@ Chocolatey is recommended and used for the following instructions. You can manua @@ -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:
```
brew install cmake ninja python3 ccache dtc git wget
brew install cmake ninja python3 ccache dtc git wget dfu-util
```
</TabItem>
@ -184,7 +190,6 @@ brew install cmake ninja python3 ccache dtc git wget @@ -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.
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop) for your operating system.
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)
@ -364,7 +369,6 @@ Since ZMK is built as a Zephyr™ application, the next step is @@ -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
Zephyr™ application is in the `app/` source directory:
#### Step into the repository
<OsTabs>
@ -449,7 +453,6 @@ If you're using Docker, you're done with setup! You must restart the container a @@ -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).
:::
#### Export Zephyr™ Core
```sh

1
docs/docs/dev-tests.md

@ -8,6 +8,7 @@ Running tests requires [native posix support](./dev-posix-board). Any folder und @@ -8,6 +8,7 @@ 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`.
## Creating a New Test Set
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
3. Modify `behavior_keymap.dtsi` to create a keymap using the behavior and related behaviors

17
docs/docs/faq.md

@ -7,7 +7,8 @@ sidebar_label: FAQs @@ -7,7 +7,8 @@ sidebar_label: FAQs
### Why Zephyr™?
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.
- 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.
@ -37,7 +38,7 @@ ZMK uses the MIT [license](https://github.com/zmkfirmware/zmk/blob/main/LICENSE) @@ -37,7 +38,7 @@ ZMK uses the MIT [license](https://github.com/zmkfirmware/zmk/blob/main/LICENSE)
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?
@ -49,26 +50,28 @@ ZMK is still in its infancy, so there’s a learning curve involved. But if you @@ -49,26 +50,28 @@ ZMK is still in its infancy, so there’s a learning curve involved. But if you
### 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:
- 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.
### 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:
- [What is a “board”?](#what-is-a-board)
- [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.
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.

14
docs/docs/feature/underglow.md

@ -36,10 +36,16 @@ If your board or shield does not have RGB underglow configured, refer to [Adding @@ -36,10 +36,16 @@ 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.
| Option | Description | Default |
| ---------------------------- | ---------------------------------------------- | ------- |
| `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_BRT_STEP` | Brightness step in percent 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_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

4
docs/docs/hardware.md

@ -28,8 +28,10 @@ That being said, there are currently only a few specific [boards](/docs/faq#what @@ -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`)
- [Sofle](https://github.com/josefadamcik/SofleKeyboard) (`sofle_left` and `sofle_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 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`)
## Other Hardware

63
docs/docs/intro.md

@ -2,42 +2,45 @@ @@ -2,42 +2,45 @@
id: intro
title: Introduction to ZMK
sidebar_label: Introduction
slug: /
---
ZMK Firmware is an open source (MIT) keyboard
firmware built on the [Zephyr™ Project](https://zephyrproject.org/) Real Time Operating System (RTOS).
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.
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.
## Features
At this point, ZMK is still missing many features. Currently, the working bits
include:
- Wireless connectivity via BLE HID Over GATT (HOG)
- USB connectivity
- Low active power usage
- Split keyboard support
- [Keymaps and layers](behavior/layers)
- [Hold-tap](behavior/hold-tap) (which includes [mod-tap](behavior/mod-tap), [layer-tap](behavior/layers))
- [Basic HID over USB](behavior/key-press)
- [Basic consumer (media) keycodes](behavior/key-press#consumer-key-press)
- [Encoders](feature/encoders)
- Basic [OLED display support](feature/displays)
- [RGB Underglow](feature/underglow)
## Missing Features
- One Shot Keys
- Combo keys
- Macros
- Complete split support (encoders and RGB are not supported on the 'peripheral' side)
- Battery reporting
- Low power sleep states
- Low power mode (to toggle LEDs and screen off)
- Shell over BLE
ZMK is currently missing some features found in other popular firmware. This table compares the features supported by ZMK, BlueMicro and QMK:
| **Feature** | ZMK | BlueMicro | QMK |
| ---------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
| Low Latency BLE Support | ✅ | ✅ | |
| Multi-Device BLE Support | ✅ | | |
| USB Connectivity | ✅ | | ✅ |
| User Configuration Repositories | ✅ | | |
| Split Keyboard Support | ✅ | ✅ | ✅ |
| [Keymaps and Layers](behavior/layers) | ✅ | ✅ | ✅ |
| [Hold-Tap](behavior/hold-tap) (which includes [Mod-Tap](behavior/mod-tap) and [Layer-Tap](behavior/layers/#layer-tap)) | ✅ | ✅ | ✅ |
| [Basic Keycodes](behavior/key-press) | ✅ | ✅ | ✅ |
| [Basic consumer (Media) Keycodes](behavior/key-press#consumer-key-press) | ✅ | ✅ | ✅ |
| [Encoders](feature/encoders)[^1] | ✅ | | ✅ |
| [OLED Display Support](feature/displays)[^2] | 🚧 | 🚧 | ✅ |
| [RGB Underglow](feature/underglow) | ✅ | ✅ | ✅ |
| One Shot Keys | 🚧 | ✅ | ✅ |
| Combo Keys | 🚧 | | ✅ |
| Macros | 🚧 | ✅ | ✅ |
| Mouse Keys | | ✅ | ✅ |
| Low Active Power Usage | ✅ | | |
| [Low Power Sleep States](https://github.com/zmkfirmware/zmk/pull/211) | 🚧 | ✅ | |
| [Low Power Mode (VCC Shutoff)](https://github.com/zmkfirmware/zmk/pull/242) | 🚧 | | |
| [Battery Reporting](https://github.com/zmkfirmware/zmk/issues/47) | 🚧 | ✅ | |
| 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

25
docs/docs/troubleshooting.md

@ -3,6 +3,7 @@ id: troubleshooting @@ -3,6 +3,7 @@ id: troubleshooting
title: Troubleshooting
sidebar_title: Troubleshooting
---
### 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).
@ -12,18 +13,17 @@ The following page provides suggestions for common errors that may occur during @@ -12,18 +13,17 @@ 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.
| ![Example Error Screen](../docs/assets/troubleshooting/filetransfer/windows.png) |
| :-------------------------------------------------------------------------------: |
| :------------------------------------------------------------------------------: |
| 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) |
| :-------------------------------------------------------------------------------: |
| :--------------------------------------------------------------------------: |
| An example of the file transfer error on MacOS |
### CMake Error
```
@ -44,24 +44,23 @@ CMake Warnings shown above during `west build` are normal occurrences. They shou @@ -44,24 +44,23 @@ 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.
For more information, click [here](../docs/dev-setup#environment-variables).
### 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.
This can be verified by checking the file in question, found in `mkdir/app/build`.
| ![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 |
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) |
| :-------------------------------------------------------------------------------: |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| 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) |
| :-------------------------------------------------------------------------------: |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| 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
@ -80,3 +79,13 @@ to avoid accidental bonding between the halves. @@ -80,3 +79,13 @@ to avoid accidental bonding between the halves.
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.
### 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" @@ -63,6 +63,7 @@ defaultValue="curl"
values={[
{label: 'Using curl', value: 'curl'},
{label: 'Using wget', value: 'wget'},
{label: 'Using PowerShell', value: 'PowerShell'},
]}>
<TabItem value="curl">
@ -74,7 +75,14 @@ bash -c "$(curl -fsSL https://zmkfirmware.dev/setup.sh)" @@ -74,7 +75,14 @@ bash -c "$(curl -fsSL https://zmkfirmware.dev/setup.sh)"
<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>

3
docs/docusaurus.config.js

@ -89,7 +89,7 @@ module.exports = { @@ -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: {
apiKey: "75325855fc90356828fe212d38e5ca34",
@ -102,7 +102,6 @@ module.exports = { @@ -102,7 +102,6 @@ module.exports = {
{
docs: {
// It is recommended to set document id as docs home page (`docs/` path).
homePageId: "intro",
sidebarPath: require.resolve("./sidebars.js"),
// Please change this to your repo.
editUrl: "https://github.com/zmkfirmware/zmk/edit/main/docs/",

24512
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

15
docs/package.json

@ -5,12 +5,14 @@ @@ -5,12 +5,14 @@
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"serve": "docusaurus serve",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy"
"deploy": "docusaurus deploy",
"clear": "docusaurus clear"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-alpha.61",
"@docusaurus/preset-classic": "^2.0.0-alpha.61",
"@docusaurus/core": "^2.0.0-alpha.66",
"@docusaurus/preset-classic": "^2.0.0-alpha.66",
"classnames": "^2.2.6",
"react": "^16.8.4",
"react-dom": "^16.8.4"
@ -26,5 +28,12 @@ @@ -26,5 +28,12 @@
"last 1 firefox 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 = { @@ -6,7 +6,7 @@ module.exports = {
"faq",
"user-setup",
"customization",
"troubleshooting"
"troubleshooting",
],
Features: [
"feature/keymaps",
@ -23,6 +23,7 @@ module.exports = { @@ -23,6 +23,7 @@ module.exports = {
"behavior/reset",
"behavior/bluetooth",
"behavior/lighting",
"behavior/power",
],
Development: [
"dev-clean-room",
@ -32,8 +33,6 @@ module.exports = { @@ -32,8 +33,6 @@ module.exports = {
"dev-posix-board",
"dev-tests",
],
"Dev Guides": [
"dev-guide-new-shield",
"dev-guide-usb-logging"],
"Dev Guides": ["dev-guide-new-shield", "dev-guide-usb-logging"],
},
};

20
docs/src/pages/index.js

@ -5,6 +5,7 @@ import Link from "@docusaurus/Link"; @@ -5,6 +5,7 @@ import Link from "@docusaurus/Link";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import useBaseUrl from "@docusaurus/useBaseUrl";
import styles from "./styles.module.css";
import PropTypes from "prop-types";
const features = [
{
@ -12,7 +13,8 @@ const features = [ @@ -12,7 +13,8 @@ const features = [
imageUrl: "img/undraw_zephyr.svg",
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 = [ @@ -20,19 +22,13 @@ const features = [
title: <>Permissive Licensing</>,
imageUrl: "img/undraw_open_source.svg",
description: (
<>
MIT licensed to remove any future limitations in innovation.
</>
<>MIT licensed to remove any future limitations in innovation.</>
),
},
{
title: <>Wireless First</>,
imageUrl: "img/undraw_wireless.svg",
description: (
<>
Designed for the future, including wireless support.
</>
),
description: <>Designed for the future, including wireless support.</>,
},
];
@ -51,6 +47,12 @@ function Feature({ imageUrl, title, description }) { @@ -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() {
const context = useDocusaurusContext();
const { siteConfig = {} } = context;

6
docs/static/setup.ps1 vendored

@ -91,9 +91,9 @@ Write-Host "Keyboard Shield Selection:" @@ -91,9 +91,9 @@ Write-Host "Keyboard Shield Selection:"
$prompt = "Pick a keyboard"
# 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"
$names = "kyria", "lily58", "corne", "splitreus62", "sofle", "iris", "romac", "m60", "microdox"
$splits = "y", "y", "y", "y", "y", "y", "n", "n", "y"
$options = "Kyria", "Lily58", "Corne", "Splitreus62", "Sofle", "Iris", "Reviung41", "RoMac", "RoMac+", "makerdiary M60", "Microdox", "TG4X", "QAZ", "NIBBLE"
$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", "n", "n", "y", "n", "n", "n"
$choice = Get-Choice-From-Options -Options $options -Prompt $prompt
$shield_title = $($options[$choice])

53
docs/static/setup.sh vendored

@ -9,15 +9,25 @@ set -e @@ -9,15 +9,25 @@ set -e
check_exists() {
command_to_run=$1
error_message=$2
local __resultvar=$3
if ! eval "$command_to_run" &> /dev/null; then
if [[ "$__resultvar" != "" ]]; then
eval $__resultvar="'false'"
else
printf "%s\n" "$error_message"
exit 1
fi
else
if [[ "$__resultvar" != "" ]]; then
eval $__resultvar="'true'"
fi
fi
}
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.email" "Git email not set!\nRun: git config --global user.email 'example@myemail.com'"
@ -29,6 +39,30 @@ if [ ! -w `pwd` ]; then @@ -29,6 +39,30 @@ if [ ! -w `pwd` ]; then
exit 1
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"
title="ZMK Config Setup:"
@ -58,7 +92,7 @@ echo "" @@ -58,7 +92,7 @@ echo ""
echo "Keyboard Shield Selection:"
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 "
# 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 @@ -73,9 +107,14 @@ select opt in "${options[@]}" "Quit"; do
4 ) shield_title="Splitreus62" shield="splitreus62"; split="y"; break;;
5 ) shield_title="Sofle" shield="sofle"; split="y"; break;;
6 ) shield_title="Iris" shield="iris"; split="y"; break;;
7 ) shield_title="RoMac" shield="romac"; split="n"; break;;
8 ) shield_title="M60" shield="m60"; split="n"; break;;
9 ) shield_title="Microdox" shield="microdox"; split="y"; break;;
7 ) shield_title="Reviung41" shield="reviung41"; split="n"; break;;
8 ) shield_title="RoMac" shield="romac"; split="n"; 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!
# $(( ${#options[@]}+1 )) ) echo "Other!"; break;;
@ -133,10 +172,10 @@ cd ${repo_name} @@ -133,10 +172,10 @@ cd ${repo_name}
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
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
popd

Loading…
Cancel
Save