Compare commits

...

10 Commits

Author SHA1 Message Date
9c1f27d103 Adding in code for jNumpad and pslgh 2023-11-24 09:05:16 +00:00
Cem Aksoylar
a3f30ee799 feat(build): Add support for artifact-name in build.yaml, correctly 2023-11-23 21:09:14 -08:00
Idan Gazit
f77e38f2b9 chore: Update devcontainer.json
The format has changed slightly.
2023-11-23 21:02:22 -08:00
Chris Andreae
0a4b1a6533 feat(ble): add behavior to disconnect from BLE profile
Adds new functionality and a behavior to disconnect an active BLE connection.
The motivation for this is that for some devices like phones, the presence of an
active BLE connection results in the onscreen keyboard being selected.
2023-11-20 15:00:10 -05:00
Alexander Krikun
d7d9eed317 feat(mouse): Initial mouse keys support.
* Add HID report/descriptor for a new report with mouse buttons,
  and x/y/wheel deltas.
* New mouse key press behavior for press/release of mouse keys.
* Add constants for HID main item values (e.g. data/array/absolute)
* Define and use constants for our HID report IDs.
2023-11-15 11:16:59 -08:00
ReFil
8776911da5 feat(ble): Allow disabling BLE BAS reporting
The battery reporting has been known to cause macOS computers to wakeup repeatedly. In some cases (e.g. display or custom lighting implementation) one might want to collect battery SOC without broadcasting over BLE

* Update docs/docs/config/battery.md

Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
2023-11-15 13:03:30 -05:00
Peter Johanson
3027b2a6e8 chore(usb): Don't enable ZMK_USB_ROOT by default.
* Some initial reports of crashes with this code enabled, so disabling
  by default for now pending further investigation.
2023-11-13 23:56:10 -08:00
Peter Johanson
f6716f869a fix(usb): Build with ZMK_USB_BOOT disabled.
* Invert the logic so `get_keyboard_report` is sane when `ZMK_USB_BOOT`
  is disabled.
2023-11-13 23:56:10 -08:00
Chris Andreae
2a1904e184 feat(boards): Add Glove80 to boards
* Add board definition for MoErgo Glove80
2023-11-14 02:08:58 -05:00
Pete Johanson
afe65ead9c Revert "feat(build): Add support for artifact-name in build.yaml"
This reverts commit c1bf35ce1d.
2023-11-13 13:51:08 -08:00
67 changed files with 1924 additions and 23 deletions

View File

@@ -13,9 +13,13 @@
"type=volume,source=zmk-zephyr-modules,target=${containerWorkspaceFolder}/modules",
"type=volume,source=zmk-zephyr-tools,target=${containerWorkspaceFolder}/tools"
],
"extensions": ["ms-vscode.cpptools"],
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
"customizations": {
"vscode": {
"extensions": ["ms-vscode.cpptools"],
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
}
}
},
"forwardPorts": [3000]
}

View File

@@ -62,7 +62,7 @@ jobs:
echo "zephyr_version=${ZEPHYR_VERSION}" >> $GITHUB_ENV
echo "extra_cmake_args=${shield:+-DSHIELD=\"$shield\"}" >> $GITHUB_ENV
echo "display_name=${shield:+$shield - }${board}" >> $GITHUB_ENV
echo "artifact_name=${artifact_name:-\"${shield:+$shield-}${board}-zmk\"}" >> $GITHUB_ENV
echo "artifact_name=${artifact_name:-${shield:+$shield-}${board}-zmk}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v3

View File

@@ -31,12 +31,14 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
target_sources(app PRIVATE src/events/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.c)
target_sources(app PRIVATE src/events/mouse_button_state_changed.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c)
target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c)
@@ -54,6 +56,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MOUSE_KEY_PRESS app PRIVATE src/behaviors/behavior_mouse_key_press.c)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c)
target_sources(app PRIVATE src/behavior_queue.c)

View File

@@ -98,7 +98,6 @@ config ZMK_USB
config ZMK_USB_BOOT
bool "USB Boot Protocol Support"
default y
depends on ZMK_USB
select USB_HID_BOOT_PROTOCOL
select USB_DEVICE_SOF
@@ -157,6 +156,10 @@ config ZMK_BLE_CONSUMER_REPORT_QUEUE_SIZE
int "Max number of consumer HID reports to queue for sending over BLE"
default 5
config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE
int "Max number of mouse HID reports to queue for sending over BLE"
default 20
config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup."
default n
@@ -316,13 +319,22 @@ endif
#Display/LED Options
endmenu
menu "Mouse Options"
config ZMK_MOUSE
bool "Enable ZMK mouse emulation"
default n
#Mouse Options
endmenu
menu "Power Management"
config ZMK_BATTERY_REPORTING
bool "Battery level detection/reporting"
default n
select SENSOR
select BT_BAS if ZMK_BLE
imply BT_BAS if ZMK_BLE
config ZMK_IDLE_TIMEOUT
int "Milliseconds of inactivity before entering idle state (OLED shutoff, etc)"

View File

@@ -6,6 +6,11 @@ config ZMK_BEHAVIOR_KEY_TOGGLE
default y
depends on DT_HAS_ZMK_BEHAVIOR_KEY_TOGGLE_ENABLED
config ZMK_BEHAVIOR_MOUSE_KEY_PRESS
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED
imply ZMK_MOUSE
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
bool

View File

@@ -0,0 +1,3 @@
zephyr_library()
zephyr_library_sources(usb_serial_number.c)
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)

View File

@@ -0,0 +1,8 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
config BOARD_ENABLE_DCDC
bool "Enable DCDC mode"
select SOC_DCDC_NRF52X
default y
depends on (BOARD_GLOVE80_LH || BOARD_GLOVE80_RH)

View File

@@ -0,0 +1,10 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
config BOARD_GLOVE80_LH
bool "Glove80 LH"
depends on SOC_NRF52840_QIAA
config BOARD_GLOVE80_RH
bool "Glove80 RH"
depends on SOC_NRF52840_QIAA

View File

@@ -0,0 +1,65 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
if BOARD_GLOVE80_LH
config BOARD
default "glove80 lh"
config ZMK_SPLIT_BLE_ROLE_CENTRAL
default y
endif # BOARD_GLOVE80_LH
if BOARD_GLOVE80_RH
config BOARD
default "glove80 rh"
endif # BOARD_GLOVE80_RH
if BOARD_GLOVE80_LH || BOARD_GLOVE80_RH
config ZMK_SPLIT
default y
config BT_CTLR
default BT
config ZMK_KSCAN_MATRIX_WAIT_BETWEEN_OUTPUTS
default 5
config PINCTRL
default y
if USB
config USB_NRFX
default y
config USB_DEVICE_STACK
default y
endif # USB
if ZMK_BACKLIGHT
config PWM
default y
config LED_PWM
default y
endif # ZMK_BACKLIGHT
if ZMK_RGB_UNDERGLOW
config SPI
default y
config WS2812_STRIP
default y
endif # ZMK_RGB_UNDERGLOW
endif # BOARD_GLOVE80_LH || BOARD_GLOVE80_RH

View File

@@ -0,0 +1,6 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
board_runner_args(nrfjprog "--nrf-family=NRF52" "--softreset")
include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2021 The ZMK Contributors
* SPDX-License-Identifier: MIT
*/
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include <dt-bindings/zmk/matrix_transform.h>
/ {
chosen {
zmk,kscan = &kscan0;
zmk,matrix_transform = &default_transform;
zephyr,code-partition = &code_partition;
zephyr,sram = &sram0;
zephyr,flash = &flash0;
zephyr,console = &cdc_acm_uart;
};
default_transform: keymap_transform_0 {
compatible = "zmk,matrix-transform";
columns = <14>;
rows = <6>;
map = <
RC(0,0) RC(0,1) RC(0,2) RC(0,3) RC(0,4) RC(0,9) RC(0,10) RC(0,11) RC(0,12) RC(0,13)
RC(1,0) RC(1,1) RC(1,2) RC(1,3) RC(1,4) RC(1,5) RC(1,8) RC(1,9) RC(1,10) RC(1,11) RC(1,12) RC(1,13)
RC(2,0) RC(2,1) RC(2,2) RC(2,3) RC(2,4) RC(2,5) RC(2,8) RC(2,9) RC(2,10) RC(2,11) RC(2,12) RC(2,13)
RC(3,0) RC(3,1) RC(3,2) RC(3,3) RC(3,4) RC(3,5) RC(3,8) RC(3,9) RC(3,10) RC(3,11) RC(3,12) RC(3,13)
RC(4,0) RC(4,1) RC(4,2) RC(4,3) RC(4,4) RC(4,5) RC(0,6) RC(1,6) RC(2,6) RC(2,7) RC(1,7) RC(0,7) RC(4,8) RC(4,9) RC(4,10) RC(4,11) RC(4,12) RC(4,13)
RC(5,0) RC(5,1) RC(5,2) RC(5,3) RC(5,4) RC(3,6) RC(4,6) RC(5,6) RC(5,7) RC(4,7) RC(3,7) RC(5,9) RC(5,10) RC(5,11) RC(5,12) RC(5,13)
>;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
debounce-press-ms = <4>;
debounce-release-ms = <20>;
};
};
&adc {
status = "okay";
};
&gpiote {
status = "okay";
};
&gpio0 {
status = "okay";
};
&gpio1 {
status = "okay";
};
&usbd {
status = "okay";
cdc_acm_uart: cdc_acm_uart {
compatible = "zephyr,cdc-acm-uart";
label = "CDC_ACM_0";
};
};
&flash0 {
/*
* For more information, see:
* http://docs.zephyrproject.org/latest/devices/dts/flash_partitions.html
*/
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
sd_partition: partition@0 {
label = "softdevice";
reg = <0x00000000 0x00026000>;
};
code_partition: partition@26000 {
label = "code_partition";
reg = <0x00026000 0x000c6000>;
};
/*
* The flash starting at 0x000ec000 and ending at
* 0x000f3fff is reserved for use by the application.
*/
/*
* Storage partition will be used by FCB/LittleFS/NVS
* if enabled.
*/
storage_partition: partition@ec000 {
label = "storage";
reg = <0x000ec000 0x00008000>;
};
boot_partition: partition@f4000 {
label = "adafruit_boot";
reg = <0x000f4000 0x0000c000>;
};
};
};

View File

@@ -0,0 +1,114 @@
/*
* 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/ext_power.h>
#include <dt-bindings/zmk/outputs.h>
#include <dt-bindings/zmk/rgb.h>
// layers
#define DEFAULT 0
#define LOWER 1
#define MAGIC 2
/ {
behaviors {
// For the "layer" key, it'd nice to be able to use it as either a shift or a toggle.
// Configure it as a tap dance, so the first tap (or hold) is a &mo and the second tap is a &to
layer_td: tap_dance_0 {
compatible = "zmk,behavior-tap-dance";
label = "LAYER_TAP_DANCE";
#binding-cells = <0>;
tapping-term-ms = <200>;
bindings = <&mo LOWER>, <&to LOWER>;
};
};
macros {
bt_0: bt_profile_macro_0 {
label = "BT_0";
compatible = "zmk,behavior-macro";
#binding-cells = <0>;
bindings
= <&out OUT_BLE>,
<&bt BT_SEL 0>;
};
bt_1: bt_profile_macro_1 {
label = "BT_1";
compatible = "zmk,behavior-macro";
#binding-cells = <0>;
bindings
= <&out OUT_BLE>,
<&bt BT_SEL 1>;
};
bt_2: bt_profile_macro_2 {
label = "BT_2";
compatible = "zmk,behavior-macro";
#binding-cells = <0>;
bindings
= <&out OUT_BLE>,
<&bt BT_SEL 2>;
};
bt_3: bt_profile_macro_3 {
label = "BT_3";
compatible = "zmk,behavior-macro";
#binding-cells = <0>;
bindings
= <&out OUT_BLE>,
<&bt BT_SEL 3>;
};
};
keymap {
compatible = "zmk,keymap";
default_layer {
// ---------------------------------------------------------------------------------------------------------------------------------
// | F1 | F2 | F3 | F4 | F5 | | F6 | F7 | F8 | F9 | F10 |
// | = | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | - |
// | TAB | Q | W | E | R | T | | Y | U | I | O | P | \ |
// | ESC | A | S | D | F | G | | H | J | K | L | ; | ' |
// | ` | Z | X | C | V | B | LSHFT | LCTRL | LOWER | | LGUI | RCTRL | RSHFT | N | M | , | . | / | PGUP |
// | MAGIC | HOME| END | LEFT | RIGHT| | BSPC | DEL | LALT | | RALT | RET | SPACE | | UP | DOWN | [ | ] | PGDN |
bindings = <
&kp F1 &kp F2 &kp F3 &kp F4 &kp F5 &kp F6 &kp F7 &kp F8 &kp F9 &kp F10
&kp EQUAL &kp N1 &kp N2 &kp N3 &kp N4 &kp N5 &kp N6 &kp N7 &kp N8 &kp N9 &kp N0 &kp MINUS
&kp TAB &kp Q &kp W &kp E &kp R &kp T &kp Y &kp U &kp I &kp O &kp P &kp BSLH
&kp ESC &kp A &kp S &kp D &kp F &kp G &kp H &kp J &kp K &kp L &kp SEMI &kp SQT
&kp GRAVE &kp Z &kp X &kp C &kp V &kp B &kp LSHFT &kp LCTRL &layer_td &kp LGUI &kp RCTRL &kp RSHFT &kp N &kp M &kp COMMA &kp DOT &kp FSLH &kp PG_UP
&mo MAGIC &kp HOME &kp END &kp LEFT &kp RIGHT &kp BSPC &kp DEL &kp LALT &kp RALT &kp RET &kp SPACE &kp UP &kp DOWN &kp LBKT &kp RBKT &kp PG_DN
>;
};
lower_layer {
bindings = <
&kp C_BRI_DN &kp C_BRI_UP &kp C_PREV &kp C_NEXT &kp C_PP &kp C_MUTE &kp C_VOL_DN &kp C_VOL_UP &none &kp PAUSE_BREAK
&trans &none &none &none &none &kp HOME &kp LPAR &kp KP_NUM &kp KP_EQUAL &kp KP_DIVIDE &kp KP_MULTIPLY &kp PSCRN
&trans &none &none &kp UP &none &kp END &kp RPAR &kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_MINUS &kp SLCK
&trans &none &kp LEFT &kp DOWN &kp RIGHT &kp PG_UP &kp PRCNT &kp KP_N4 &kp KP_N5 &kp KP_N6 &kp KP_PLUS &none
&trans &kp K_CMENU &none &kp F11 &kp F12 &kp PG_DN &trans &trans &to DEFAULT &trans &trans &trans &kp COMMA &kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_ENTER &trans
&trans &kp CAPS &kp INS &kp F11 &kp F12 &trans &trans &trans &trans &trans &trans &kp KP_N0 &kp KP_N0 &kp KP_DOT &kp KP_ENTER &trans
>;
};
magic_layer {
bindings = <
&bt BT_CLR &none &none &none &none &none &none &none &none &none
&none &none &none &none &none &none &none &none &none &none &none &none
&none &rgb_ug RGB_SPI &rgb_ug RGB_SAI &rgb_ug RGB_HUI &rgb_ug RGB_BRI &rgb_ug RGB_TOG &none &none &none &none &none &none
&bootloader &rgb_ug RGB_SPD &rgb_ug RGB_SAD &rgb_ug RGB_HUD &rgb_ug RGB_BRD &rgb_ug RGB_EFF &none &none &none &none &none &bootloader
&sys_reset &none &none &none &none &none &bt_2 &bt_3 &none &none &none &none &none &none &none &none &none &sys_reset
&none &none &none &none &none &bt_0 &bt_1 &out OUT_USB &none &none &none &none &none &none &none &none
>;
};
};
};

View File

@@ -0,0 +1,19 @@
identifier: glove80
name: Glove80
url: https://www.moergo.com/
type: mcu
arch: arm
toolchain:
- zephyr
- gnuarmemb
- xtools
supported:
- adc
- usb_device
- ble
- ieee802154
- pwm
- watchdog
- gpio
- i2c
- spi

View File

@@ -0,0 +1,16 @@
file_format: "1"
id: glove80
name: Glove80
type: board
arch: arm
url: https://www.moergo.com/
features:
- keys
- underglow
- backlight
outputs:
- usb
- ble
siblings:
- glove80_lh
- glove80_rh

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2021 The ZMK Contributors
* SPDX-License-Identifier: MIT
*/
&pinctrl {
spi3_default: spi3_default {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 27)>; // WS2812_VEXT_DATA
};
};
spi3_sleep: spi3_sleep {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 27)>;
low-power-enable;
};
};
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 15)>; // rear LED
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 1, 15)>;
bias-pull-down;
};
};
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 22)>, // EXT1
<NRF_PSEL(UART_RX, 0, 21)>; // EXT2
};
};
uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 22)>,
<NRF_PSEL(UART_RX, 0, 21)>;
low-power-enable;
};
};
};

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "glove80.dtsi"
#include "glove80_lh-pinctrl.dtsi"
#include <dt-bindings/led/led.h>
/ {
model = "glove80_lh";
compatible = "glove80_lh";
chosen {
zmk,underglow = &led_strip;
zmk,backlight = &back_led_backlight;
zmk,battery = &vbatt;
};
back_led_backlight: pwmleds {
compatible = "pwm-leds";
label = "BACK LED";
pwm_led_0 {
pwms = <&pwm0 0 PWM_USEC(20) PWM_POLARITY_NORMAL>;
label = "Back LED configured as backlight";
};
};
ext-power {
compatible = "zmk,ext-power-generic";
label = "EXT_POWER";
control-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; /* WS2812_CE */
init-delay-ms = <100>;
};
vbatt: vbatt {
compatible = "zmk,battery-nrf-vddh";
label = "BATTERY";
};
};
&spi3 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi3_default>;
pinctrl-1 = <&spi3_sleep>;
pinctrl-names = "default", "sleep";
led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";
label = "WS2812C-2020";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <4000000>;
/* WS2812 */
chain-length = <40>; /* 40 keys have underglow at the moment */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
};
};
&pwm0 {
status = "okay";
pinctrl-0 = <&pwm0_default>;
pinctrl-1 = <&pwm0_sleep>;
pinctrl-names = "default", "sleep";
};
&uart0 {
compatible = "nordic,nrf-uarte";
pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep";
};
&kscan0 {
row-gpios
= <&gpio0 26 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW1
, <&gpio0 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW2
, <&gpio0 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW3
, <&gpio0 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW4
, <&gpio0 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW5
, <&gpio1 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // LH ROW6
;
col-gpios
= <&gpio1 8 GPIO_ACTIVE_HIGH> // LH COL6
, <&gpio1 4 GPIO_ACTIVE_HIGH> // LH COL5
, <&gpio1 6 GPIO_ACTIVE_HIGH> // LH COL4
, <&gpio1 7 GPIO_ACTIVE_HIGH> // LH COL3
, <&gpio1 5 GPIO_ACTIVE_HIGH> // LH COL2
, <&gpio1 3 GPIO_ACTIVE_HIGH> // LH COL1
, <&gpio1 1 GPIO_ACTIVE_HIGH> // LH Thumb
;
};

View File

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

View File

@@ -0,0 +1,92 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
CONFIG_SOC_SERIES_NRF52X=y
CONFIG_SOC_NRF52840_QIAA=y
CONFIG_BOARD_GLOVE80_LH=y
# Enable both USB and BLE
CONFIG_ZMK_USB=y
CONFIG_ZMK_BLE=y
# Keyboard IDs
CONFIG_ZMK_KEYBOARD_NAME="Glove80 Left"
CONFIG_USB_DEVICE_PID=0x27db
CONFIG_USB_DEVICE_VID=0x16c0
CONFIG_USB_DEVICE_MANUFACTURER="MoErgo"
CONFIG_USB_DEVICE_SN="moergo.com:GLV80-0123456789ABCDEF"
CONFIG_BT_DIS_PNP_PID=0x27db
CONFIG_BT_DIS_PNP_VID=0x16c0
CONFIG_BT_DIS_MANUF="MoErgo"
CONFIG_BT_DIS_MODEL="Glove80"
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
# Work-around for Windows bug with battery notifications
CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n
# Enable MPU
CONFIG_ARM_MPU=y
# Enable GPIO
CONFIG_GPIO=y
# Build configurations
CONFIG_BUILD_OUTPUT_UF2=y
CONFIG_BUILD_OUTPUT_UF2_FAMILY_ID="0x9807B007"
CONFIG_USE_DT_CODE_PARTITION=y
# Flash configuration
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
# Enable 32kHz crystal
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y
# Enable RGB underglow
CONFIG_ZMK_RGB_UNDERGLOW=y
CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER=y
CONFIG_ZMK_RGB_UNDERGLOW_ON_START=n
CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP=4
CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN=4
# DO NOT CHANGE CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX TO ABOVE 80. Configuring
# BRT_MAX above 80% will draw additional current and can potentially damage your
# computer. WARRANTY IS VOID IF BRT_MAX SET ABOVE 80.
CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX=80
CONFIG_ZMK_RGB_UNDERGLOW_EFF_START=3
CONFIG_ZMK_RGB_UNDERGLOW_HUE_START=285
CONFIG_ZMK_RGB_UNDERGLOW_SAT_START=75
CONFIG_ZMK_RGB_UNDERGLOW_BRT_START=16
# The power LED is implemented as a backlight
# For now, the power LED is acting as a "USB connected" indicator
CONFIG_ZMK_BACKLIGHT=y
CONFIG_ZMK_BACKLIGHT_ON_START=y
CONFIG_ZMK_BACKLIGHT_BRT_START=5
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE=y
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB=y
# The full two-byte consumer report space has compatibility issues with some
# operating systems, most notably macOS. Use the more basic single-byte usage
# space.
CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC=y
# Turn on debugging to disable optimization. Debug messages can result in larger
# stacks, so enable stack protection and particularly a larger BLE peripheral stack.
# CONFIG_DEBUG=y
# CONFIG_DEBUG_THREAD_INFO=y
# CONFIG_EXCEPTION_STACK_TRACE=y
# CONFIG_HW_STACK_PROTECTION=y
# CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE=1300
# Log via USB or Segger RTT
CONFIG_ZMK_USB_LOGGING=n
CONFIG_ZMK_RTT_LOGGING=n

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2021 The ZMK Contributors
* SPDX-License-Identifier: MIT
*/
&pinctrl {
spi3_default: spi3_default {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 13)>; // WS2812_VEXT_DATA
};
};
spi3_sleep: spi3_sleep {
group1 {
psels = <NRF_PSEL(SPIM_MOSI, 0, 13)>;
low-power-enable;
};
};
pwm0_default: pwm0_default {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 16)>; // Rear LED
};
};
pwm0_sleep: pwm0_sleep {
group1 {
psels = <NRF_PSEL(PWM_OUT0, 0, 16)>;
bias-pull-down;
};
};
uart0_default: uart0_default {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 21)>, // EXT1
<NRF_PSEL(UART_RX, 0, 24)>; // EXT2
};
};
uart0_sleep: uart0_sleep {
group1 {
psels = <NRF_PSEL(UART_TX, 0, 21)>,
<NRF_PSEL(UART_RX, 0, 24)>;
low-power-enable;
};
};
};

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "glove80.dtsi"
#include "glove80_rh-pinctrl.dtsi"
#include <dt-bindings/led/led.h>
/ {
model = "glove80_rh";
compatible = "glove80_rh";
chosen {
zmk,underglow = &led_strip;
zmk,backlight = &back_led_backlight;
zmk,battery = &vbatt;
};
back_led_backlight: pwmleds {
compatible = "pwm-leds";
label = "BACK LED";
pwm_led_0 {
pwms = <&pwm0 0 PWM_USEC(20) PWM_POLARITY_NORMAL>;
label = "Back LED configured as backlight";
};
};
ext-power {
compatible = "zmk,ext-power-generic";
label = "EXT_POWER";
control-gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; /* WS2812_CE */
init-delay-ms = <100>;
};
vbatt: vbatt {
compatible = "zmk,battery-nrf-vddh";
label = "BATTERY";
};
};
&spi3 {
compatible = "nordic,nrf-spim";
status = "okay";
pinctrl-0 = <&spi3_default>;
pinctrl-1 = <&spi3_sleep>;
pinctrl-names = "default", "sleep";
led_strip: ws2812@0 {
compatible = "worldsemi,ws2812-spi";
label = "WS2812C-2020";
/* SPI */
reg = <0>; /* ignored, but necessary for SPI bindings */
spi-max-frequency = <4000000>;
/* WS2812 */
chain-length = <40>; /* 40 keys have underglow at the moment */
spi-one-frame = <0x70>;
spi-zero-frame = <0x40>;
color-mapping = <LED_COLOR_ID_GREEN LED_COLOR_ID_RED LED_COLOR_ID_BLUE>;
};
};
&pwm0 {
status = "okay";
pinctrl-0 = <&pwm0_default>;
pinctrl-1 = <&pwm0_sleep>;
pinctrl-names = "default", "sleep";
};
&uart0 {
compatible = "nordic,nrf-uarte";
pinctrl-0 = <&uart0_default>;
pinctrl-1 = <&uart0_sleep>;
pinctrl-names = "default", "sleep";
};
/* For right hand, the columns are offset by 7 */
&default_transform {
col-offset = <7>;
};
&kscan0 {
row-gpios
= <&gpio0 26 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW1
, <&gpio0 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW2
, <&gpio0 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW3
, <&gpio1 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW4
, <&gpio0 11 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW5
, <&gpio0 12 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // RH ROW6
;
col-gpios
= <&gpio1 6 GPIO_ACTIVE_HIGH> // RH Thumb
, <&gpio1 4 GPIO_ACTIVE_HIGH> // RH COL1
, <&gpio0 2 GPIO_ACTIVE_HIGH> // RH COL2
, <&gpio1 7 GPIO_ACTIVE_HIGH> // RH COL3
, <&gpio1 5 GPIO_ACTIVE_HIGH> // RH COL4
, <&gpio1 3 GPIO_ACTIVE_HIGH> // RH COL5
, <&gpio1 1 GPIO_ACTIVE_HIGH> // RH COL6
;
};

View File

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

View File

@@ -0,0 +1,89 @@
# Copyright (c) 2021 The ZMK Contributors
# SPDX-License-Identifier: MIT
CONFIG_SOC_SERIES_NRF52X=y
CONFIG_SOC_NRF52840_QIAA=y
CONFIG_BOARD_GLOVE80_RH=y
# Enable both USB and BLE
CONFIG_ZMK_USB=y
CONFIG_ZMK_BLE=y
# Keyboard IDs
CONFIG_ZMK_KEYBOARD_NAME="Glove80 Right"
CONFIG_USB_DEVICE_PID=0x27d9
CONFIG_USB_DEVICE_VID=0x16c0
CONFIG_USB_DEVICE_MANUFACTURER="MoErgo"
CONFIG_USB_DEVICE_SN="moergo.com:GLV80-0123456789ABCDEF"
CONFIG_BT_DIS_PNP_PID=0x27d9
CONFIG_BT_DIS_PNP_VID=0x16c0
CONFIG_BT_DIS_MANUF="MoErgo"
CONFIG_BT_DIS_MODEL="Glove80 Right"
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
# Enable MPU
CONFIG_ARM_MPU=y
# Enable GPIO
CONFIG_GPIO=y
# Build configurations
CONFIG_BUILD_OUTPUT_UF2=y
CONFIG_BUILD_OUTPUT_UF2_FAMILY_ID="0x9808B007"
CONFIG_USE_DT_CODE_PARTITION=y
# Flash configuration
CONFIG_MPU_ALLOW_FLASH_WRITE=y
CONFIG_NVS=y
CONFIG_SETTINGS_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
# Enable 32kHz crystal
CONFIG_CLOCK_CONTROL_NRF_K32SRC_XTAL=y
# Enable RGB underglow
CONFIG_ZMK_RGB_UNDERGLOW=y
CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER=y
CONFIG_ZMK_RGB_UNDERGLOW_ON_START=n
CONFIG_ZMK_RGB_UNDERGLOW_BRT_STEP=4
CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN=4
# DO NOT CHANGE CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX TO ABOVE 80. Configuring
# BRT_MAX above 80% will draw additional current and can potentially damage your
# computer. WARRANTY IS VOID IF BRT_MAX SET ABOVE 80.
CONFIG_ZMK_RGB_UNDERGLOW_BRT_MAX=80
CONFIG_ZMK_RGB_UNDERGLOW_EFF_START=3
CONFIG_ZMK_RGB_UNDERGLOW_HUE_START=285
CONFIG_ZMK_RGB_UNDERGLOW_SAT_START=75
CONFIG_ZMK_RGB_UNDERGLOW_BRT_START=16
# The power LED is implemented as a backlight
# For now, the power LED is acting as a "USB connected" indicator
CONFIG_ZMK_BACKLIGHT=y
CONFIG_ZMK_BACKLIGHT_ON_START=y
CONFIG_ZMK_BACKLIGHT_BRT_START=5
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_IDLE=y
CONFIG_ZMK_BACKLIGHT_AUTO_OFF_USB=y
# The full two-byte consumer report space has compatibility issues with some
# operating systems, most notably macOS. Use the more basic single-byte usage
# space.
CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC=y
# Turn on debugging to disable optimization. Debug messages can result in larger
# stacks, so enable stack protection and particularly a larger BLE peripheral stack.
# CONFIG_DEBUG=y
# CONFIG_DEBUG_THREAD_INFO=y
# CONFIG_EXCEPTION_STACK_TRACE=y
# CONFIG_HW_STACK_PROTECTION=y
# CONFIG_ZMK_SPLIT_BLE_PERIPHERAL_STACK_SIZE=1300
# Log via USB or Segger RTT
CONFIG_ZMK_USB_LOGGING=n
CONFIG_ZMK_RTT_LOGGING=n

View File

@@ -0,0 +1,13 @@
## MoErgo Glove80
This board definition provides ZMK support for the [MoErgo Glove80](https://www.moergo.com)
keyboard.
MoErgo additionally offers a customized version of ZMK which adds additional functionality such as
RGB status indicators, available on GitHub at [moergo-sc/zmk](https://github.com/moergo-sc/zmk). The
MoErgo customized ZMK fork is regularly updated to include the latest changes from mainline ZMK, but
will not always be completely up-to-date. MoErgo also offers an online layout configurator and
firmware builder application using the customized fork at [my.glove80.com](https://my.glove80.com).
While mainline ZMK is expected to work well with Glove80, MoErgo only provides support for use of
their customized fork. Likewise, the ZMK community cannot directly provide support for MoErgo's fork.

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <string.h>
#include <zephyr/usb/usb_device.h>
#include <zephyr/drivers/hwinfo.h>
#include "usb_descriptor.h"
#define LOG_LEVEL CONFIG_USB_DEVICE_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(usb_descriptor);
int base16_encode(const uint8_t *data, int length, uint8_t *result, int bufSize);
uint8_t *usb_update_sn_string_descriptor(void) {
/*
* nrf52840 hwinfo returns a 64-bit hardware id. Glove80 uses this as a
* serial number, encoded as base16 into the last 16 characters of the
* CONFIG_USB_DEVICE_SN template. If insufficient template space is
* available, instead return the static serial number string.
*/
const uint8_t template_len = sizeof(CONFIG_USB_DEVICE_SN);
const uint8_t sn_len = 16;
if (template_len < sn_len + 1) {
LOG_DBG("Serial number template too short");
return CONFIG_USB_DEVICE_SN;
}
static uint8_t serial[sizeof(CONFIG_USB_DEVICE_SN)];
strncpy(serial, CONFIG_USB_DEVICE_SN, template_len);
uint8_t hwid[8];
memset(hwid, 0, sizeof(hwid));
uint8_t hwlen = hwinfo_get_device_id(hwid, sizeof(hwid));
if (hwlen > 0) {
const uint8_t offset = template_len - sn_len - 1;
LOG_HEXDUMP_DBG(&hwid, sn_len, "Serial Number");
base16_encode(hwid, hwlen, serial + offset, sn_len + 1);
}
return serial;
}
int base16_encode(const uint8_t *data, int length, uint8_t *result, int bufSize) {
const char hex[] = "0123456789ABCDEF";
int i = 0;
while (i < bufSize && i < length * 2) {
uint8_t nibble;
if (i % 2 == 0) {
nibble = data[i / 2] >> 4;
} else {
nibble = data[i / 2] & 0xF;
}
result[i] = hex[nibble];
++i;
}
if (i < bufSize) {
result[i] = '\0';
}
return i;
}

View File

@@ -0,0 +1,40 @@
# Copyright (c) 2020 Pete Johanson, Richard Jones
# SPDX-License-Identifier: MIT
if SHIELD_JNUMPAD
config ZMK_KEYBOARD_NAME
default "jNumpad Wireless"
endif
if ZMK_DISPLAY
config I2C
default y
config SSD1306
default y
config SSD1306_REVERSE_MODE
default y
endif # ZMK_DISPLAY
if LVGL
config LV_Z_VDB_SIZE
default 64
config LV_Z_DPI
default 148
config LV_Z_BITS_PER_PIXEL
default 1
choice LV_COLOR_DEPTH
default LV_COLOR_DEPTH_1
endchoice
endif # LVGL

View File

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

View File

@@ -0,0 +1,7 @@
CONFIG_ZMK_DISPLAY=y
CONFIG_ZMK_DISPLAY_STATUS_SCREEN_BUILT_IN=y
CONFIG_ZMK_DISPLAY_WORK_QUEUE_SYSTEM=y
CONFIG_ZMK_WIDGET_BATTERY_STATUS=y
CONFIG_ZMK_WIDGET_BATTERY_STATUS_SHOW_PERCENTAGE=y
CONFIG_ZMK_WIDGET_WPM_STATUS=y
CONFIG_ZMK_EXT_POWER=y

View File

@@ -0,0 +1,48 @@
/ {
chosen {
zmk,kscan = &kscan0;
zephyr,display = &oled;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&pro_micro 9 GPIO_ACTIVE_HIGH>
, <&pro_micro 8 GPIO_ACTIVE_HIGH>
, <&pro_micro 7 GPIO_ACTIVE_HIGH>
, <&pro_micro 6 GPIO_ACTIVE_HIGH>
;
};
};
&pro_micro_i2c {
status = "okay";
oled: ssd1306@3c {
compatible = "solomon,ssd1306fb";
reg = <0x3c>;
label = "DISPLAY";
width = <128>;
height = <32>;
segment-offset = <0>;
page-offset = <0>;
display-offset = <0>;
multiplex-ratio = <31>;
segment-remap;
com-invdir;
com-sequential;
prechargep = <0x22>;
};
};

View File

@@ -0,0 +1,73 @@
// numpad.keymap
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/bt.h>
#include <dt-bindings/zmk/ext_power.h>
/ {
combos {
compatible = "zmk,combos";
combo_ext_pwr {
timeout-ms = <50>;
key-positions = <1 2>;
bindings = <&ext_power EP_TOG>;
};
combo_bt_prv {
timeout-ms = <50>;
key-positions = <1 3>;
bindings = <&bt BT_PRV>;
};
combo_bt_nxt {
timeout-ms = <50>;
key-positions = <1 7>;
bindings = <&bt BT_NXT>;
};
combo_reset {
timeout-ms = <50>;
key-positions = <1 15>;
bindings = <&sys_reset>;
};
combo_btclr {
timeout-ms = <50>;
key-positions = <1 16>;
bindings = <&bt BT_CLR>;
};
combo_btldr {
timeout-ms = <50>;
key-positions = <1 18>;
bindings = <&bootloader>;
};
};
};
/ {
keymap {
compatible = "zmk,keymap";
default_layer {
// ┌───┬───┬───┬───┐
// │NUM│ / │ * │ - │
// ├───┼───┼───┼───┤
// │ 7 │ 8 │ 9 │ + │
// ├───┼───┼───┼─ ─┤
// │ 4 │ 5 │ 6 │ │
// ├───┼───┼───┼───┤
// │ 1 │ 2 │ 3 │RET│
// ├───┴───┼───┼─ ─┤
// │ 0 │ . │ │
// └───────┴───┴───┘
bindings = <
&kp KP_NUM &kp KP_DIVIDE &kp KP_MULTIPLY &kp KP_MINUS
&kp KP_N7 &kp KP_N8 &kp KP_N9 &kp KP_PLUS
&kp KP_N4 &kp KP_N5 &kp KP_N6 &trans
&kp KP_N1 &kp KP_N2 &kp KP_N3 &kp KP_ENTER
&kp KP_N0 &trans &kp KP_DOT &trans
>;
};
};
};

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 Pete Johanson, Richard Jones
*
* SPDX-License-Identifier: MIT
*/
#include <dt-bindings/zmk/matrix_transform.h>
#include "jnumpad.dtsi"
/ {
chosen {
zmk,kscan = &kscan0;
zephyr,display = &oled;
};
kscan0: kscan {
compatible = "zmk,kscan-gpio-matrix";
label = "KSCAN";
diode-direction = "col2row";
row-gpios
= <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&pro_micro 9 GPIO_ACTIVE_HIGH>
, <&pro_micro 8 GPIO_ACTIVE_HIGH>
, <&pro_micro 7 GPIO_ACTIVE_HIGH>
, <&pro_micro 6 GPIO_ACTIVE_HIGH>
;
};
};

View File

@@ -0,0 +1,10 @@
# Copyright (c) 2020 Pete Johanson, Richard Jones
# SPDX-License-Identifier: MIT
if SHIELD_PSLGH
config ZMK_KEYBOARD_NAME
default "PSLGH"
endif

View File

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

View File

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020 Pete Johanson, Richard Jones
*
* 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 {
// -------------------
// | | | | | A | S | J | K | L |
// | UP |DOWN |ENTER| H | | | | | |
// ----------------------
bindings = <
&trans &trans &trans &trans &kp A &kp S &kp J &kp K &kp L
&trans &trans &kp RET &kp H &trans &trans &trans &trans &trans
&kp UARW &kp DARW &trans &trans &trans &trans &trans &trans &trans
>;
};
};
};

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2020 Pete Johanson, Richard Jones
*
* 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_d 2 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
, <&pro_micro_d 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
;
col-gpios
= <&pro_micro_a 1 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 2 GPIO_ACTIVE_HIGH>
, <&pro_micro_a 0 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 14 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 7 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 6 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 5 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 4 GPIO_ACTIVE_HIGH>
, <&pro_micro_d 3 GPIO_ACTIVE_HIGH>
;
};
bt_unpair_combo: bt_unpair_combo {
compatible = "zmk,bt-unpair-combo";
key-positions = <0 11>;
};
};
/*
x x
x x
r1 x
a down
k up
j enter
s r2
a h
x x
x r3
*/

View File

@@ -18,4 +18,5 @@
#include <behaviors/caps_word.dtsi>
#include <behaviors/key_repeat.dtsi>
#include <behaviors/backlight.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_key_press.dtsi>

View File

@@ -0,0 +1,9 @@
/ {
behaviors {
/omit-if-no-ref/ mkp: behavior_mouse_key_press {
compatible = "zmk,behavior-mouse-key-press";
label = "MOUSE_KEY_PRESS";
#binding-cells = <1>;
};
};
};

View File

@@ -0,0 +1,5 @@
description: Mouse key press/release behavior
compatible: "zmk,behavior-mouse-key-press"
include: one_param.yaml

View File

@@ -9,6 +9,7 @@
#define BT_PRV_CMD 2
#define BT_SEL_CMD 3
// #define BT_FULL_RESET_CMD 4
#define BT_DISC_CMD 5
/*
Note: Some future commands will include additional parameters, so we
@@ -19,3 +20,4 @@ defines these aliases up front.
#define BT_NXT BT_NXT_CMD 0
#define BT_PRV BT_PRV_CMD 0
#define BT_SEL BT_SEL_CMD
#define BT_DISC BT_DISC_CMD

View File

@@ -26,6 +26,7 @@
#define HID_USAGE_GDV (0x06) // Generic Device Controls
#define HID_USAGE_KEY (0x07) // Keyboard/Keypad
#define HID_USAGE_LED (0x08) // LED
#define HID_USAGE_BUTTON (0x09) // Button
#define HID_USAGE_TELEPHONY (0x0B) // Telephony Device
#define HID_USAGE_CONSUMER (0x0C) // Consumer
#define HID_USAGE_DIGITIZERS (0x0D) // Digitizers

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zephyr/dt-bindings/dt-util.h>
/* Mouse press behavior */
/* Left click */
#define MB1 BIT(0)
#define LCLK (MB1)
/* Right click */
#define MB2 BIT(1)
#define RCLK (MB2)
/* Middle click */
#define MB3 BIT(2)
#define MCLK (MB3)
#define MB4 BIT(3)
#define MB5 BIT(4)

View File

@@ -24,6 +24,7 @@ int zmk_ble_clear_bonds();
int zmk_ble_prof_next();
int zmk_ble_prof_prev();
int zmk_ble_prof_select(uint8_t index);
int zmk_ble_prof_disconnect(uint8_t index);
int zmk_ble_active_profile_index();
bt_addr_le_t *zmk_ble_active_profile_addr();

View File

@@ -69,3 +69,7 @@ int zmk_endpoints_toggle_transport(void);
struct zmk_endpoint_instance zmk_endpoints_selected(void);
int zmk_endpoints_send_report(uint16_t usage_page);
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_endpoints_send_mouse_report();
#endif // IS_ENABLE(CONFIG_ZMK_MOUSE)

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <zmk/hid.h>
#include <zmk/event_manager.h>
#include <zmk/mouse.h>
struct zmk_mouse_button_state_changed {
zmk_mouse_button_t buttons;
bool state;
int64_t timestamp;
};
ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed);
static inline struct zmk_mouse_button_state_changed_event *
zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) {
return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){
.buttons = ZMK_HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp});
}

View File

@@ -10,10 +10,15 @@
#include <zephyr/usb/class/usb_hid.h>
#include <zmk/keys.h>
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
#include <zmk/mouse.h>
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#include <dt-bindings/zmk/hid_usage.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
#define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL
#define ZMK_HID_MOUSE_NUM_BUTTONS 0x05
// See https://www.usb.org/sites/default/files/hid1_11.pdf section 6.2.2.4 Main Items
@@ -46,6 +51,7 @@
#define ZMK_HID_REPORT_ID_KEYBOARD 0x01
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03
static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
@@ -114,6 +120,39 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_ARRAY | ZMK_HID_MAIN_VAL_ABS),
HID_END_COLLECTION,
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
HID_USAGE_PAGE(HID_USAGE_GD),
HID_USAGE(HID_USAGE_GD_MOUSE),
HID_COLLECTION(HID_COLLECTION_APPLICATION),
HID_REPORT_ID(ZMK_HID_REPORT_ID_MOUSE),
HID_USAGE(HID_USAGE_GD_POINTER),
HID_COLLECTION(HID_COLLECTION_PHYSICAL),
HID_USAGE_PAGE(HID_USAGE_BUTTON),
HID_USAGE_MIN8(0x1),
HID_USAGE_MAX8(ZMK_HID_MOUSE_NUM_BUTTONS),
HID_LOGICAL_MIN8(0x00),
HID_LOGICAL_MAX8(0x01),
HID_REPORT_SIZE(0x01),
HID_REPORT_COUNT(0x5),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
// Constant padding for the last 3 bits.
HID_REPORT_SIZE(0x03),
HID_REPORT_COUNT(0x01),
HID_INPUT(ZMK_HID_MAIN_VAL_CONST | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
// Some OSes ignore pointer devices without X/Y data.
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
HID_USAGE(HID_USAGE_GD_X),
HID_USAGE(HID_USAGE_GD_Y),
HID_USAGE(HID_USAGE_GD_WHEEL),
HID_LOGICAL_MIN8(-0x7F),
HID_LOGICAL_MAX8(0x7F),
HID_REPORT_SIZE(0x08),
HID_REPORT_COUNT(0x03),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),
HID_END_COLLECTION,
HID_END_COLLECTION,
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
};
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
@@ -163,6 +202,21 @@ struct zmk_hid_consumer_report {
struct zmk_hid_consumer_report_body body;
} __packed;
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report_body {
zmk_mouse_button_flags_t buttons;
int8_t d_x;
int8_t d_y;
int8_t d_wheel;
} __packed;
struct zmk_hid_mouse_report {
uint8_t report_id;
struct zmk_hid_mouse_report_body body;
} __packed;
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
zmk_mod_flags_t zmk_hid_get_explicit_mods();
int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier);
@@ -189,9 +243,21 @@ int zmk_hid_press(uint32_t usage);
int zmk_hid_release(uint32_t usage);
bool zmk_hid_is_pressed(uint32_t usage);
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hid_mouse_button_press(zmk_mouse_button_t button);
int zmk_hid_mouse_button_release(zmk_mouse_button_t button);
int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons);
int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons);
void zmk_hid_mouse_clear();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report();
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
zmk_hid_boot_report_t *zmk_hid_get_boot_report();
#endif
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View File

@@ -13,3 +13,7 @@ int zmk_hog_init();
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

12
app/include/zmk/mouse.h Normal file
View File

@@ -0,0 +1,12 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <dt-bindings/zmk/mouse.h>
typedef uint8_t zmk_mouse_button_flags_t;
typedef uint16_t zmk_mouse_button_t;

View File

@@ -10,4 +10,7 @@
int zmk_usb_hid_send_keyboard_report();
int zmk_usb_hid_send_consumer_report();
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
void zmk_usb_hid_set_protocol(uint8_t protocol);

View File

@@ -51,7 +51,7 @@ static int zmk_battery_update(const struct device *battery) {
if (last_state_of_charge != state_of_charge.val1) {
last_state_of_charge = state_of_charge.val1;
#if IS_ENABLED(CONFIG_BT_BAS)
LOG_DBG("Setting BAS GATT battery level to %d.", last_state_of_charge);
rc = bt_bas_set_battery_level(last_state_of_charge);
@@ -60,7 +60,7 @@ static int zmk_battery_update(const struct device *battery) {
LOG_WRN("Failed to set BAS GATT battery level (err %d)", rc);
return rc;
}
#endif
rc = ZMK_EVENT_RAISE(new_zmk_battery_state_changed(
(struct zmk_battery_state_changed){.state_of_charge = last_state_of_charge}));
}

View File

@@ -31,6 +31,8 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
return zmk_ble_prof_prev();
case BT_SEL_CMD:
return zmk_ble_prof_select(binding->param2);
case BT_DISC_CMD:
return zmk_ble_prof_disconnect(binding->param2);
default:
LOG_ERR("Unknown BT command: %d", binding->param1);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_mouse_key_press
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zmk/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/events/mouse_button_state_changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
static int behavior_mouse_key_press_init(const struct device *dev) { return 0; };
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
return ZMK_EVENT_RAISE(
zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp));
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
return ZMK_EVENT_RAISE(
zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp));
}
static const struct behavior_driver_api behavior_mouse_key_press_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
#define MKP_INST(n) \
DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, NULL, NULL, NULL, APPLICATION, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_mouse_key_press_driver_api);
DT_INST_FOREACH_STATUS_OKAY(MKP_INST)
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@@ -271,6 +271,27 @@ int zmk_ble_prof_prev() {
ZMK_BLE_PROFILE_COUNT);
};
int zmk_ble_prof_disconnect(uint8_t index) {
if (index >= ZMK_BLE_PROFILE_COUNT)
return -ERANGE;
bt_addr_le_t *addr = &profiles[index].peer;
struct bt_conn *conn;
int result;
if (!bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) {
return -ENODEV;
} else if ((conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr)) == NULL) {
return -ENODEV;
}
result = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
LOG_DBG("Disconnected from profile %d: %d", index, result);
bt_conn_unref(conn);
return result;
}
bt_addr_le_t *zmk_ble_active_profile_addr() { return &profiles[active_profile].peer; }
char *zmk_ble_active_profile_name() { return profiles[active_profile].name; }

View File

@@ -191,6 +191,36 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
return -ENOTSUP;
}
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_endpoints_send_mouse_report() {
switch (current_instance.transport) {
#if IS_ENABLED(CONFIG_ZMK_USB)
case ZMK_TRANSPORT_USB: {
int err = zmk_usb_hid_send_mouse_report();
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_TRANSPORT_BLE: {
struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report();
int err = zmk_hog_send_mouse_report(&mouse_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
}
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
}
LOG_ERR("Unsupported endpoint transport %d", current_instance.transport);
return -ENOTSUP;
}
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_SETTINGS)
static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb,
@@ -295,6 +325,9 @@ static int zmk_endpoints_init(const struct device *_arg) {
static void disconnect_current_endpoint() {
zmk_hid_keyboard_clear();
zmk_hid_consumer_clear();
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
zmk_hid_mouse_clear();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
zmk_endpoints_send_report(HID_USAGE_KEY);
zmk_endpoints_send_report(HID_USAGE_CONSUMER);

View File

@@ -0,0 +1,9 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zmk/events/mouse_button_state_changed.h>
ZMK_EVENT_IMPL(zmk_mouse_button_state_changed);

View File

@@ -12,6 +12,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <dt-bindings/zmk/modifiers.h>
static struct zmk_hid_keyboard_report keyboard_report = {
.report_id = ZMK_HID_REPORT_ID_KEYBOARD, .body = {.modifiers = 0, ._reserved = 0, .keys = {0}}};
static struct zmk_hid_consumer_report consumer_report = {.report_id = ZMK_HID_REPORT_ID_CONSUMER,
@@ -24,6 +25,13 @@ static uint8_t keys_held = 0;
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static struct zmk_hid_mouse_report mouse_report = {.report_id = ZMK_HID_REPORT_ID_MOUSE,
.body = {.buttons = 0}};
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
// Keep track of how often a modifier was pressed.
// Only release the modifier if the count is 0.
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@@ -357,6 +365,71 @@ bool zmk_hid_is_pressed(uint32_t usage) {
return false;
}
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
// Keep track of how often a button was pressed.
// Only release the button if the count is 0.
static int explicit_button_counts[5] = {0, 0, 0, 0, 0};
static zmk_mod_flags_t explicit_buttons = 0;
#define SET_MOUSE_BUTTONS(btns) \
{ \
mouse_report.body.buttons = btns; \
LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \
}
int zmk_hid_mouse_button_press(zmk_mouse_button_t button) {
if (button >= ZMK_HID_MOUSE_NUM_BUTTONS) {
return -EINVAL;
}
explicit_button_counts[button]++;
LOG_DBG("Button %d count %d", button, explicit_button_counts[button]);
WRITE_BIT(explicit_buttons, button, true);
SET_MOUSE_BUTTONS(explicit_buttons);
return 0;
}
int zmk_hid_mouse_button_release(zmk_mouse_button_t button) {
if (button >= ZMK_HID_MOUSE_NUM_BUTTONS) {
return -EINVAL;
}
if (explicit_button_counts[button] <= 0) {
LOG_ERR("Tried to release button %d too often", button);
return -EINVAL;
}
explicit_button_counts[button]--;
LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]);
if (explicit_button_counts[button] == 0) {
LOG_DBG("Button %d released", button);
WRITE_BIT(explicit_buttons, button, false);
}
SET_MOUSE_BUTTONS(explicit_buttons);
return 0;
}
int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) {
for (zmk_mouse_button_t i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
if (buttons & BIT(i)) {
zmk_hid_mouse_button_press(i);
}
}
return 0;
}
int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) {
for (zmk_mouse_button_t i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
if (buttons & BIT(i)) {
zmk_hid_mouse_button_release(i);
}
}
return 0;
}
void zmk_hid_mouse_clear() { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
return &keyboard_report;
}
@@ -364,3 +437,11 @@ struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report() {
return &consumer_report;
}
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report() {
return &mouse_report;
}
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View File

@@ -56,6 +56,15 @@ static struct hids_report consumer_input = {
.type = HIDS_INPUT,
};
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static struct hids_report mouse_input = {
.id = ZMK_HID_REPORT_ID_MOUSE,
.type = HIDS_INPUT,
};
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
static bool host_requests_notification = false;
static uint8_t ctrl_point;
// static uint8_t proto_mode;
@@ -93,6 +102,15 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
sizeof(struct zmk_hid_consumer_report_body));
}
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) {
struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body;
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
sizeof(struct zmk_hid_mouse_report_body));
}
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
// static ssize_t write_proto_mode(struct bt_conn *conn,
// const struct bt_gatt_attr *attr,
// const void *buf, uint16_t len, uint16_t offset,
@@ -139,6 +157,15 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &consumer_input),
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL),
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &mouse_input),
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point));
@@ -261,6 +288,59 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
return 0;
};
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body),
CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4);
void send_mouse_report_callback(struct k_work *work) {
struct zmk_hid_mouse_report_body report;
while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) {
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return;
}
struct bt_gatt_notify_params notify_params = {
.attr = &hog_svc.attrs[13],
.data = &report,
.len = sizeof(report),
};
int err = bt_gatt_notify_cb(conn, &notify_params);
if (err) {
LOG_DBG("Error notifying %d", err);
}
bt_conn_unref(conn);
}
};
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return 1;
}
struct bt_gatt_notify_params notify_params = {
.attr = &hog_svc.attrs[13],
.data = report,
.len = sizeof(*report),
};
int err = bt_gatt_notify_cb(conn, &notify_params);
if (err) {
LOG_DBG("Error notifying %d", err);
return err;
}
bt_conn_unref(conn);
return 0;
};
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hog_init(const struct device *_arg) {
static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"};
k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack),

43
app/src/mouse.c Normal file
View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/events/mouse_button_state_changed.h>
#include <zmk/hid.h>
#include <zmk/endpoints.h>
#include <zmk/mouse.h>
static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) {
LOG_DBG("buttons: 0x%02X", ev->buttons);
zmk_hid_mouse_buttons_press(ev->buttons);
zmk_endpoints_send_mouse_report();
}
static void listener_mouse_button_released(const struct zmk_mouse_button_state_changed *ev) {
LOG_DBG("buttons: 0x%02X", ev->buttons);
zmk_hid_mouse_buttons_release(ev->buttons);
zmk_endpoints_send_mouse_report();
}
int mouse_listener(const zmk_event_t *eh) {
const struct zmk_mouse_button_state_changed *mbt_ev = as_zmk_mouse_button_state_changed(eh);
if (mbt_ev) {
if (mbt_ev->state) {
listener_mouse_button_pressed(mbt_ev);
} else {
listener_mouse_button_released(mbt_ev);
}
return 0;
}
return 0;
}
ZMK_LISTENER(mouse_listener, mouse_listener);
ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed);

View File

@@ -39,16 +39,16 @@ void zmk_usb_hid_set_protocol(uint8_t protocol) { hid_protocol = protocol; }
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
static uint8_t *get_keyboard_report(size_t *len) {
if (hid_protocol == HID_PROTOCOL_REPORT) {
struct zmk_hid_keyboard_report *report = zmk_hid_get_keyboard_report();
*len = sizeof(*report);
return (uint8_t *)report;
}
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
zmk_hid_boot_report_t *boot_report = zmk_hid_get_boot_report();
*len = sizeof(*boot_report);
return (uint8_t *)boot_report;
if (hid_protocol != HID_PROTOCOL_REPORT) {
zmk_hid_boot_report_t *boot_report = zmk_hid_get_boot_report();
*len = sizeof(*boot_report);
return (uint8_t *)boot_report;
}
#endif
struct zmk_hid_keyboard_report *report = zmk_hid_get_keyboard_report();
*len = sizeof(*report);
return (uint8_t *)report;
}
static int get_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len,
@@ -129,6 +129,19 @@ int zmk_usb_hid_send_consumer_report() {
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
}
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report() {
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
if (hid_protocol == HID_PROTOCOL_BOOT) {
return -ENOTSUP;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
struct zmk_hid_mouse_report *report = zmk_hid_get_mouse_report();
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
}
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
static int zmk_usb_hid_init(const struct device *_arg) {
hid_dev = device_get_binding("HID_0");
if (hid_dev == NULL) {

View File

@@ -0,0 +1 @@
s/.*zmk_hid_mouse_button_//p

View File

@@ -0,0 +1,10 @@
press: Button 0 count 1
press: Mouse buttons set to 0x01
press: Button 1 count 1
press: Mouse buttons set to 0x03
release: Button 1 count: 0
release: Button 1 released
release: Mouse buttons set to 0x01
release: Button 0 count: 0
release: Button 0 released
release: Mouse buttons set to 0x00

View File

@@ -0,0 +1,28 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>
/ {
keymap {
compatible = "zmk,keymap";
label = "Default keymap";
default_layer {
bindings = <
&mkp LCLK &none
&none &mkp RCLK
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS (0,0,100)
ZMK_MOCK_PRESS (1,1,100)
ZMK_MOCK_RELEASE(1,1, 10)
ZMK_MOCK_RELEASE(0,0, 10)
>;
};

View File

@@ -0,0 +1,28 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>
/ {
keymap {
compatible = "zmk,keymap";
label = "Default keymap";
default_layer {
bindings = <
&mkp LCLK &none
&none &mkp RCLK
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS (0,0,100)
ZMK_MOCK_PRESS (1,1,100)
ZMK_MOCK_RELEASE(1,1, 10)
ZMK_MOCK_RELEASE(0,0, 10)
>;
};

View File

@@ -13,6 +13,13 @@ computer/laptop/keyboard should receive the keyboard input; many of the commands
When pairing to a host device ZMK saves bond information to the selected profile. It will not replace this when you initiate pairing with another device. To pair with a new device select an unused profile with `BT_SEL`, `BT_NXT` or `BT_PRV` bindings, or by clearing an existing profile using `BT_CLR`.
A ZMK device may show as "connected" on multiple hosts at the same time. This is working as intended, and only the host associated with the active profile will receive keystrokes.
An _inactive_ connected profile can be explicitly disconnected using the `BT_DISC` behavior. This can be helpful in
cases when host devices behave differently when a bluetooth keyboard is connected, for example by hiding their on-screen
keyboard. Note that at present the active bluetooth profile will immediately reconnect if disconnected. This is true
even if OUT_USB is selected. To remain disconnected, another bluetooth profile must be first selected using (e.g.)
`BT_SEL`.
:::
## Bluetooth Command Defines
@@ -28,12 +35,14 @@ This will allow you to reference the actions defined in this header such as `BT_
Here is a table describing the command for each define:
| Define | Action |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BT_CLR` | Clear bond information between the keyboard and host for the selected profile. |
| `BT_NXT` | Switch to the next profile, cycling through to the first one when the end is reached. |
| `BT_PRV` | Switch to the previous profile, cycling through to the last one when the beginning is reached. |
| `BT_SEL` | Select the 0-indexed profile by number. Please note: this definition must include a number as an argument in the keymap to work correctly. eg. `BT_SEL 0` |
| Define | Action |
| --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BT_CLR` | Clear bond information between the keyboard and host for the selected profile. |
| `BT_NXT` | Switch to the next profile, cycling through to the first one when the end is reached. |
| `BT_PRV` | Switch to the previous profile, cycling through to the last one when the beginning is reached. |
| `BT_SEL` | Select the 0-indexed profile by number. Please note: this definition must include a number as an argument in the keymap to work correctly. eg. `BT_SEL 0` |
| `BT_DISC` | Disconnect from the 0-indexed profile by number, if it's currently connected and inactive. Please note: this definition must include a number as an |
| | argument in the keymap to work correctly. eg. `BT_DISC 0` |
:::note Selected profile persistence
The profile that is selected by the `BT_SEL`/`BT_PRV`/`BT_NXT` actions will be saved to flash storage and hence persist across restarts and firmware flashes.

View File

@@ -0,0 +1,66 @@
---
title: Mouse Emulation Behaviors
sidebar_label: Mouse Emulation
---
## Summary
Mouse emulation behaviors send mouse events. Currently, only mouse button presses are supported, but movement
and scroll action support is planned for the future.
Whenever the Mouse Emulation feature is turned on or off, the HID protocol used to communicate events to hosts changes. Unfortunately, those changes are not always detected automatically, and might require re-pairing your keyboard to your devices to work over bluetooth. If mouse behaviors are still not recognized by your device after doing that, you can try [these troubleshooting steps](../features/bluetooth.md#windows-connected-but-not-working).
## Configuration Option
This feature can be enabled or disabled explicitly via a config option:
```
CONFIG_ZMK_MOUSE=y
```
If you use the mouse key press behavior in your keymap, the feature will automatically be enabled for you.
## Mouse Button Defines
To make it easier to encode the HID mouse button numeric values, include
the [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) header
provided by ZMK near the top:
```
#include <dt-bindings/zmk/mouse.h>
```
## Mouse Button Press
This behavior can press/release up to 5 mouse buttons.
### Behavior Binding
- Reference: `&mkp`
- Parameter: A `uint8` with bits 0 through 4 each referring to a button.
The following defines can be passed for the parameter:
| Define | Action |
| :------------ | :------------- |
| `MB1`, `LCLK` | Left click |
| `MB2`, `RCLK` | Right click |
| `MB3`, `MCLK` | Middle click |
| `MB4` | Mouse button 4 |
| `MB5` | Mouse button 5 |
Mouse buttons 4 and 5 typically map to "back" and "forward" actions in most applications.
### Examples
The following will send a left click press when the binding is triggered:
```
&mkp LCLK
```
This example will send press of the fourth mouse button when the binding is triggered:
```
&mkp MB4
```

View File

@@ -22,6 +22,12 @@ While `CONFIG_ZMK_BATTERY_REPORTING` is disabled by default it is implied by `CO
:::
:::note BLE reporting on MacOS
On macOS the BLE battery reporting packets can cause the computer to wakeup from sleep. To prevent this, the battery _reporting_ service can be disabled by setting `CONFIG_BT_BAS=n`. This setting is independent of battery _monitoring_, for instance the battery level can still be indicated on a display.
:::
### Devicetree
Applies to: [`/chosen` node](https://docs.zephyrproject.org/latest/guides/dts/intro.html#aliases-and-chosen-nodes)

View File

@@ -66,6 +66,7 @@ for more information on configuring Bluetooth.
| Config | Type | Description | Default |
| ------------------------------------------- | ---- | --------------------------------------------------------------------- | ------- |
| `CONFIG_BT` | bool | Enable Bluetooth support | |
| `CONFIG_BT_BAS` | bool | Enable the Bluetooth BAS (battery reporting service) | y |
| `CONFIG_BT_MAX_CONN` | int | Maximum number of simultaneous Bluetooth connections | 5 |
| `CONFIG_BT_MAX_PAIRED` | int | Maximum number of paired Bluetooth devices | 5 |
| `CONFIG_ZMK_BLE` | bool | Enable ZMK as a Bluetooth keyboard | |

View File

@@ -36,6 +36,7 @@ module.exports = {
"behaviors/caps-word",
"behaviors/key-repeat",
"behaviors/sensor-rotate",
"behaviors/mouse-emulation",
"behaviors/reset",
"behaviors/bluetooth",
"behaviors/outputs",