Skip to main content

Adding Soft Off To A Keyboard

Advanced methods of adding soft off to a keyboard are detailed below. The first two tabs describe methods involving hardware changes, while the last describes the firmware changes necessary to define a single specific key switch for waking up.

Hardware Changes

Add a direct push button between a GPIO pin and ground. This button will act as an on/off switch.

Alternatively, if you wish to integrate a dedicated GPIO pin into a key switch combination using a direct kscan, tie all of the MCU pins that you wish to combine to the dedicated GPIO pin through an OR gate. All firmware changes then follow identically to the direct push button.

Firmware Changes

Several items work together to make both triggering soft off properly, and setting up the device to wake from soft off work as expected.

Soft off behavior instance

Behind the scenes, a hardware dedicated GPIO pin utilizes the soft off behavior to trigger entering the soft-off state. To use said behavior outside of a keymap, add an instance of the behavior to your .overlay/.dts file:

/ {
behaviors {
hw_soft_off: hw_soft_off {
compatible = "zmk,behavior-soft-off";
#binding-cells = <0>;
split-peripheral-off-on-press; // Turn peripheral off immediately for reliability
hold-time-ms = <2000>; // Only turn off if the key is held for 2 seconds or longer.
};
};
};

GPIO key

Zephyr's basic GPIO Key concept is used to configure the soft off GPIO pin.

Here is an example for a keyboard with a dedicated on/off push button that is a direct wire between the GPIO pin and ground:

/ {
keys {
compatible = "gpio-keys";
soft_off_gpio_key: soft_off_gpio_key {
gpios = <&gpio0 2 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
};
};
};

In the above example the soft on/off would be triggered by pulling the specified pin low, typically by pressing a switch that has the other leg connected to ground.

GPIO keys are defined using child nodes under the gpio-keys compatible node. Each child needs just one property defined:

  • The gpios property should be a phandle-array with a fully defined GPIO pin and with the correct pull up/down and active high/low flags set.

KScan sideband behavior

The kscan sideband behavior driver will be used to trigger the soft off behavior "out of band" from the normal keymap processing. To do so, it will decorate/wrap an underlying kscan driver.

With a simple direct pin setup, the direct kscan driver can be used with a GPIO key, to make a small "side matrix":

/ {
wakeup_scan: wakeup_scan {
compatible = "zmk,kscan-gpio-direct";
input-keys = <&soft_off_gpio_key>;
wakeup-source;
};
};

With that in place, the kscan sideband behavior will wrap the new driver:

/ {
side_band_behavior_triggers: side_band_behavior_triggers {
compatible = "zmk,kscan-sideband-behaviors";

kscan = <&wakeup_scan>;
auto-enable;
wakeup-source;

soft_off {
column = <0>;
row = <0>;
bindings = <&hw_soft_off>;
};
};
};

As the kscan used only has a single key, both column and row are set to 0. The properties of the kscan-sideband-behaviors node can be found in the appropriate configuration section.

Finally, we will list the wakeup_scan device in an additional configuration section so that the ZMK soft off process knows it needs to enable this device as part of the soft off processing so it can wake the keyboard from soft off when pressed:

/ {
soft_off_wakers {
compatible = "zmk,soft-off-wakeup-sources";
wakeup-sources = <&wakeup_scan>;
};
};

Here are the properties for the node:

  • The compatible property for the node must be zmk,soft-off-wakeup-sources.
  • The wakeup-sources property is a phandle array pointing to all the devices that should be enabled during the shutdown process to be sure they can later wake the keyboard.
tip

If you add your kscan to the wakeup-sources array, then your keyboard will wake upon pressing any key in your kscan. Essentially, this causes &soft_off to behave like a behavior that puts the keyboard in deep sleep. If you choose to do so, then you can omit everything aside from the soft_off_wakers node.