Move docs/ to docs/en/
It allows comparison between the directories and helps translations.
This commit is contained in:
committed by
Kyle Brown
parent
885359a8f4
commit
0ab6887fbe
83
docs/en/Getting_Started.md
Normal file
83
docs/en/Getting_Started.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Getting Started
|
||||
> Life was like a box of chocolates. You never know what you're gonna get.
|
||||
|
||||
KMK is a keyboard focused layer that sits on top of [CircuitPython](https://circuitpython.org/). As such, it should work with most [boards that support CircuitPython](https://circuitpython.org/downloads). KMK requires CircuitPython version 7.0 or above.
|
||||
Known working and recommended devices can be found [here](Officially_Supported_Microcontrollers.md)
|
||||
|
||||
<br>
|
||||
|
||||
## TL;DR Quick start guide
|
||||
> To infinity and beyond!
|
||||
1. [Install CircuitPython version 7.0 or higher on your board](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython). With most boards, it should be as easy as drag and dropping the firmware on the drive
|
||||
2. Get a [copy of KMK](https://github.com/KMKfw/kmk_firmware/archive/refs/heads/master.zip) from the master branch
|
||||
3. Unzip it and copy the KMK folder and the boot.py file at the root of the USB drive corresponding to your board (often appearing as CIRCUITPY)
|
||||
4. Create a new *code.py* or *main.py* file in the same root directory (same level as boot.py) with the example content hereunder:
|
||||
|
||||
***IMPORTANT:*** adapt the GP0 / GP1 pins to your specific board ! <br>
|
||||
|
||||
```
|
||||
print("Starting")
|
||||
|
||||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.keys import KC
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
|
||||
keyboard.col_pins = (board.GP0,)
|
||||
keyboard.row_pins = (board.GP1,)
|
||||
keyboard.diode_orientation = DiodeOrientation.COL2ROW
|
||||
|
||||
keyboard.keymap = [
|
||||
[KC.A,]
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
keyboard.go()
|
||||
```
|
||||
|
||||
|
||||
5. With a wire / paperclip / whatever, connect the pins you selected for col_pin and row_pin together.
|
||||
|
||||

|
||||
|
||||
6. If it prints the letter "a" (or a "Q" or ... depending on your keyboard layout), you're done!
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
## Now that you're up and running, you may want to go further...
|
||||
> This is your last chance. After this, there is no turning back. You take the blue pill—the story ends, you wake up in your bed and believe whatever you want to believe. You take the red pill—you stay in Wonderland, and I show you how deep the rabbit hole goes. Remember: all I'm offering is the truth. Nothing more.
|
||||
|
||||
### You're extremely lucky and you have a fully supported keyboard
|
||||
If your keyboard and microcontroller are officially supported, simply visit the page for your files, and dropping them on the root of the "flash drive". Those pages can be found [here](https://github.com/KMKfw/kmk_firmware/tree/master/boards). You will need the `kb.py` and `main.py`. More advanced instructions can be found [here](config_and_keymap.md).
|
||||
|
||||
### You've got another, maybe DIY, board and want to customize KMK for it
|
||||
First, be sure to understand how your device work, and particularly its specific matrix configuration. You can have a look [here](http://pcbheaven.com/wikipages/How_Key_Matrices_Works/) or read the [guide](https://docs.qmk.fm/#/hand_wire) provided by the QMK team for handwired keyboards
|
||||
<br>Once you've got the gist of it:
|
||||
- You can have a look [here](config_and_keymap.md) and [here](keys.md) to start customizing your code.py / main.py file
|
||||
- There's a [reference](keycodes.md) of the available keycodes
|
||||
- [International](international.md) extension adds keys for non US layouts and [Media Keys](media_keys.md) adds keys for ... media
|
||||
|
||||
And to go even further:
|
||||
- [Sequences](sequences.md) are used for sending multiple keystrokes in a single action
|
||||
- [Layers](layers.md) can transform the whole way your keyboard is behaving with a single touch
|
||||
- [ModTap](modtap.md) allow you to customize the way a key behaves whether it is tapped or hold, and [TapDance](tapdance.md) depending on the number of times it is pressed
|
||||
|
||||
Want to have fun features such as RGB, split keyboards and more? Check out what builtin [modules](modules.md) and [extensions](extensions.md) can do!
|
||||
You can also get ideas from the various [user examples](https://github.com/KMKfw/kmk_firmware/tree/master/user_keymaps) that we provide and dig into our [documentation](README.md).
|
||||
|
||||
<br>
|
||||
|
||||
## Additional help and support
|
||||
> Roads? Where we're going we don't need roads.
|
||||
|
||||
In case you need it, debugging help can be found [here](debugging.md)
|
||||
|
||||
If you need support with KMK or just want to say hi, find us in
|
||||
[#kmkfw:klar.sh on Matrix](https://matrix.to/#/#kmkfw:klar.sh). This channel is
|
||||
bridged to Discord [here](https://discordapp.com/widget?id=493256121075761173&theme=dark)
|
||||
for convenience. If you ask for help in chat or open a bug report, if possible
|
||||
make sure your copy of KMK is up-to-date.
|
67
docs/en/Officially_Supported_Microcontrollers.md
Normal file
67
docs/en/Officially_Supported_Microcontrollers.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Officially supported microcontrollers
|
||||
While most CircuitPython devices are great for hand wired keyboards, most
|
||||
keyboards are designed to accept a Pro Micro. The boards listed below either
|
||||
are, or can be adapted to that pinout to use common keyboards already on the market.
|
||||
|
||||
## nice!nano
|
||||
Features include
|
||||
- Pro Micro pinout
|
||||
- Both USB HID and Bluetooth support
|
||||
- Can do Bluetooth split keyboards with no wires at all
|
||||
- Has battery support including charging
|
||||
|
||||
Downsides
|
||||
- $25 USD per microcontroller at most retailers
|
||||
- Not enough space to run KMK without compiling
|
||||
|
||||
### Pre-compiling KMK for nice!nano
|
||||
As the nice!nano has limited flash memory you'll need to compile KMK. To do that you'll need to download and install the [compatible mpy-cross](https://adafruit-circuit-python.s3.amazonaws.com/index.html?prefix=bin/mpy-cross/) for your Operating System. Don't forget to add it to your PATH, test by running `mpy-cross` from a shell (Powershell, Bash, Fish, etc). Once that's set up, run either `make compile` (if you have `make`) or `python util/compile.py`to generate the `.mpy` versions of KMK files. Then copy the whole compiled `kmk/` directory to your keyboard.
|
||||
|
||||
|
||||
Common Retailers
|
||||
- [Boardsource](https://boardsource.xyz/store/5f4a1733bbaa5c635b83ed67)
|
||||
- [Nice Keyboards](https://nicekeyboards.com/nice-nano/)
|
||||
|
||||
## ItsyBitsy M4 Express
|
||||
Features include
|
||||
- Affordable at $15 USD
|
||||
- Can run most features of KMK including RGB
|
||||
|
||||
Downsides
|
||||
- Needs adapted to work with Pro Micro pinout keyboards. Adapter can be found
|
||||
[HERE](https://github.com/KMKfw/kmk_firmware/tree/master/hardware)
|
||||
|
||||
Common Retailers
|
||||
- [Adafruit](https://www.adafruit.com/product/3800)
|
||||
|
||||
## RP2040
|
||||
Features include
|
||||
- Very affordable
|
||||
- Very powerful for the price
|
||||
|
||||
Downsides
|
||||
- Little support for keyboard kits
|
||||
|
||||
Common Retailers
|
||||
- [Adafruit](https://www.adafruit.com/pico?src=raspberrypi)
|
||||
- [SparkFun](https://www.sparkfun.com/products/17829?src=raspberrypi)
|
||||
|
||||
## Adafruit ItsyBitsy nRF52840 Express
|
||||
Features include
|
||||
- Both USB HID and Bluetooth support
|
||||
- More affordable than the nice!nano at only $18
|
||||
|
||||
Downsides
|
||||
- Needs adapted to work with Pro Micro pinout keyboards. Adapter can be found
|
||||
[HERE](https://github.com/KMKfw/kmk_firmware/tree/master/hardware)
|
||||
- No battery support without addon board found
|
||||
[HERE](https://www.adafruit.com/product/2124)
|
||||
|
||||
Common Retailers
|
||||
- [Adafruit](https://www.adafruit.com/product/4481)
|
||||
|
||||
## Other microcontrollers
|
||||
What you'll need to have at minimum
|
||||
- CircuitPython
|
||||
- 256KB of flash storage
|
||||
- HID over USB and/or Bluetooth.
|
54
docs/en/README.md
Normal file
54
docs/en/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Documentation index
|
||||
|
||||
> Before you look further, you probably want to start with our [getting started guide](https://github.com/KMKfw/kmk_firmware/blob/master/docs/Getting_Started.md)
|
||||
|
||||
## Basics
|
||||
|
||||
- [Getting Started](Getting_Started.md)
|
||||
- [Bluetooth HID](ble_hid.md): Connect keyboard to PC or mobile device using BLE
|
||||
- [Configuring KMK](config_and_keymap.md)
|
||||
- [Porting to KMK](porting_to_kmk.md): Creating a `kb.py` file for a board
|
||||
- [Debugging](debugging.md)
|
||||
- [Keycodes](keycodes.md): List of all available keycodes
|
||||
- [Officially supported microcontrollers](Officially_Supported_Microcontrollers.md)
|
||||
- [Support](support.md)
|
||||
- [Contributing](contributing.md)
|
||||
|
||||
## Advanced
|
||||
|
||||
- [Flashing instructions](flashing.md)
|
||||
- [Handwiring](handwiring.md): Resources helpful when handwiring a keyboard circuit
|
||||
- [Keys](keys.md): Technical explanation of key handling
|
||||
- [Scanners](scanners.md): Setting up non-default key reading
|
||||
|
||||
## [Modules](modules.md)
|
||||
|
||||
- [Combos](combos.md): Adds chords and sequences
|
||||
- [Layers](layers.md): Adds layer support (Fn key) to allow many more keys to be put on your keyboard
|
||||
- [ModTap](modtap.md): Adds support for augmented modifier keys to act as one key when tapped, and modifier when held.
|
||||
- [Mouse keys](mouse_keys.md): Adds mouse keycodes
|
||||
- [OneShot](oneshot.md): Adds support for oneshot/sticky keys.
|
||||
- [Power](power.md): Power saving features. This is mostly useful when on battery power.
|
||||
- [SerialACE](serialace.md): [DANGER - _see module README_] Arbitrary Code Execution over the data serial.
|
||||
- [Split](split_keyboards.md): Keyboards split in two. Seems ergonomic!
|
||||
- [TapDance](tapdance.md): Different key actions depending on how often it is pressed.
|
||||
|
||||
### Peripherals
|
||||
|
||||
- [ADNS9800](adns9800.md): Controlling ADNS9800 optical sensor
|
||||
- [Encoder](encoder.md): Handling rotary encoders
|
||||
- [Pimoroni trackball](pimoroni_trackball.md): Handling a small I2C trackball made by Pimoroni
|
||||
|
||||
## [Extensions](extensions.md)
|
||||
|
||||
- [International](international.md): Adds international keycodes
|
||||
- [LED](led.md): Adds backlight support. This is for monocolor backlight, not RGB
|
||||
- [LockStatus](lock_status.md): Exposes host-side locks like caps or num lock.
|
||||
- [MediaKeys](media_keys.md): Adds support for media keys such as volume
|
||||
- [RGB](rgb.md): RGB lighting for underglow. Will work on most matrix RGB as will be treated the same as underglow.
|
||||
- [Status LED](extension_statusled.md): Indicates which layer you are on with an array of single leds.
|
||||
|
||||
## Language versions
|
||||
|
||||
- [Japanese getting started](ja/Getting_Started.md)
|
||||
- [Brazilian Portuguese](https://github.com/KMKfw/kmk_firmware/tree/master/docs/ptBR)
|
19
docs/en/adns9800.md
Normal file
19
docs/en/adns9800.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# ADNS9800
|
||||
Add this module for controlling ADNS9800 optical sensor.
|
||||
```python
|
||||
from kmk.modules.adns9800 import ADNS9800
|
||||
keyboard.modules.append(ADNS9800(cs=board.GP0, sclk=board.GP2, miso=board.GP4, mosi=board.GP3, invert_y=True))
|
||||
```
|
||||
|
||||
Firmware for this sensor has to be obtained separately and placed in `kmk\modules\adns9800_firmware.py`
|
||||
```python
|
||||
firmware = (
|
||||
b'\x03'
|
||||
b'\xa6'
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## Constructor parameters
|
||||
|
||||
ADNS9800(cs=*cs_pin*, sclk=*clock_pin*, miso=*miso_pin*, mosi=*mosi_pin*, invert_x=*False*, invert_y=*False*)
|
33
docs/en/ble_hid.md
Normal file
33
docs/en/ble_hid.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# BLE HID
|
||||
Bluetooth connections help clean up the wire mess!
|
||||
|
||||
## CircuitPython
|
||||
If not running KMKPython, this does require the adafruit_ble library from Adafruit.
|
||||
This can be downloaded
|
||||
[here](https://github.com/adafruit/Adafruit_CircuitPython_BLE/tree/master/adafruit_ble).
|
||||
It is part of the [Adafruit CircuitPython Bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle).
|
||||
Simply put this in the "root" of your CircuitPython device. If unsure, it's the folder with main.py in it, and should be the first folder you see when you open the device.
|
||||
|
||||
## Enabling BLE
|
||||
|
||||
To enable BLE hid, change the keyboard.go(). By default, the advertised name
|
||||
will be the name of the "flash drive". By default this is CIRCUITPY
|
||||
|
||||
```python
|
||||
from kmk.hid import HIDModes
|
||||
|
||||
if __name__ == '__main__':
|
||||
keyboard.go(hid_type=HIDModes.BLE)
|
||||
```
|
||||
|
||||
## Changing the advertise name
|
||||
There are two ways to change the advertising name. The first would be to
|
||||
[change the name of the drive](https://learn.adafruit.com/welcome-to-circuitpython/the-circuitpy-drive).
|
||||
The second would be to change the keyboard.go() like this.
|
||||
|
||||
```python
|
||||
if __name__ == '__main__':
|
||||
keyboard.go(hid_type=HIDModes.BLE, ble_name='KMKeyboard')
|
||||
```
|
||||
|
||||
|
65
docs/en/boot.md
Normal file
65
docs/en/boot.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# boot.py
|
||||
`boot.py` lives in the root of your keyboard when mounted as a storage device.
|
||||
There is a more detailed explanation in the [circuit python docs](https://docs.circuitpython.org/en/latest/README.html),
|
||||
however there are some common use cases for your keyboard listed here.
|
||||
|
||||
## Hiding device storage
|
||||
You can hide your device from showing up as a USB storage by default (this can be overridden
|
||||
at startup if desired, per the example code further down this page).
|
||||
|
||||
```python
|
||||
storage.disable_usb_drive()
|
||||
```
|
||||
|
||||
## Using your keyboard before the OS loads
|
||||
You can make your keyboard work in your computer's BIOS and bootloader (useful if you dual-boot).
|
||||
|
||||
```python
|
||||
usb_hid.enable(boot_device=1)
|
||||
```
|
||||
|
||||
## Disabling serial communication
|
||||
By default, you can connect to your board's serial console, which can be useful for debugging,
|
||||
but may not be desired for everyday use.
|
||||
|
||||
```python
|
||||
# Equivalent to usb_cdc.enable(console=False, data=False)
|
||||
usb_cdc.disable()
|
||||
```
|
||||
|
||||
## Example code
|
||||
Below is a fully working example, which disables USB storage, CDC and enables BIOS mode.
|
||||
|
||||
```python
|
||||
import supervisor
|
||||
import board
|
||||
import digitalio
|
||||
import storage
|
||||
import usb_cdc
|
||||
import usb_hid
|
||||
|
||||
from kb import KMKKeyboard
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
|
||||
# If this key is held during boot, don't run the code which hides the storage and disables serial
|
||||
# This will use the first row/col pin. Feel free to change it if you want it to be another pin
|
||||
col = digitalio.DigitalInOut(KMKKeyboard.col_pins[0])
|
||||
row = digitalio.DigitalInOut(KMKKeyboard.row_pins[0])
|
||||
|
||||
if KMKKeyboard.diode_orientation == DiodeOrientation.COLUMNS:
|
||||
col.switch_to_output(value=True)
|
||||
row.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
else:
|
||||
col.switch_to_input(pull=digitalio.Pull.DOWN)
|
||||
row.switch_to_output(value=True)
|
||||
|
||||
if not row.value:
|
||||
storage.disable_usb_drive()
|
||||
# Equivalent to usb_cdc.enable(console=False, data=False)
|
||||
usb_cdc.disable()
|
||||
usb_hid.enable(boot_device=1)
|
||||
|
||||
row.deinit()
|
||||
col.deinit()
|
||||
```
|
27
docs/en/capsword.md
Normal file
27
docs/en/capsword.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# CapsWord
|
||||
The CapsWord module functions similar to caps lock but will deactivate automatically when its encounters a key that breaks the word or after inactivity timeout.
|
||||
By default it will not deactivate CapsWord on numbers, alphabets, underscore, modifiers, minus, backspace and other keys like ModTap, Layers, etc.
|
||||
Add it to your keyboard's modules list with:
|
||||
|
||||
```python
|
||||
from kmk.modules.capsword import CapsWord
|
||||
# default inactivity timeout is 8s
|
||||
caps_word=CapsWord()
|
||||
# change inactivity timeout
|
||||
# caps_word=CapsWord(timeout=5000)
|
||||
# for no inactivity timeout
|
||||
# caps_word=CapsWord(timeout=0)
|
||||
# add additional ignored keys
|
||||
# caps_word.keys_ignored.append(KC.COMMA)
|
||||
keyboard.modules.append(caps_word)
|
||||
keyboard.keymap = [
|
||||
[
|
||||
KC.CW,
|
||||
],
|
||||
]
|
||||
```
|
||||
## Keycodes
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------|--------------------|-----------------------------------------------|
|
||||
|`KC.CW` |`KC.CAPSWORD` |Enables/disables CapsWord |
|
21
docs/en/cg_swap.md
Normal file
21
docs/en/cg_swap.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Ctrl GUI Swap
|
||||
This module allows to swap Ctrl with GUI and vice versa. This will reset on restart to the default implementation
|
||||
|
||||
## Enabling the module
|
||||
```python
|
||||
from kmk.modules.cg_swap import CgSwap
|
||||
# cg_swap disabled on startup
|
||||
cg_swap = CgSwap()
|
||||
# cg_swap enabled on startup
|
||||
# cg_swap = CgSwap(cg_swap_enable=True)
|
||||
keyboard.modules.append(cg_swap)
|
||||
|
||||
keyboard.keymap = [
|
||||
[
|
||||
KC.CG_SWAP, # swap ctrl and gui
|
||||
KC.CG_NORM, # unswap ctrl and gui
|
||||
KC.CG_TOGG, # toggles ctrl and gui swap
|
||||
],
|
||||
]
|
||||
```
|
||||
|
52
docs/en/combos.md
Normal file
52
docs/en/combos.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Combos
|
||||
Combos allow you to assign special functionality to combinations of key presses.
|
||||
The two default behaviors are:
|
||||
* Chords: match keys in random order, all pressed within 50ms.
|
||||
* Sequences: match keys in order, pressed within 1s of one another.
|
||||
|
||||
You can define combos to listen to any valid KMK key, even internal or
|
||||
functional keys, like HoldTap. When using internal KMK keys, be aware that the
|
||||
order of modules matters.
|
||||
|
||||
The result of a combo is another key being pressed/released; if the desired
|
||||
action isn't covered by KMK keys: create your own with `make_key` and attach
|
||||
corresponding handlers.
|
||||
|
||||
Combos may overlap, i.e. share match keys amongst each other.
|
||||
|
||||
## Keycodes
|
||||
|New Keycode |Description |
|
||||
|------------|----------------------------------------------------|
|
||||
|`KC.LEADER` | a dummy / convenience key for leader key sequences |
|
||||
|
||||
## Custom Combo Behavior
|
||||
Optional arguments that customize individual combos:
|
||||
* `fast_reset`: If True, allows tapping every key (default for sequences);
|
||||
if False, allows holding at least one key and tapping the rest to repeatedly
|
||||
activate the combo (default for chords).
|
||||
* `per_key_timeout`: If True, reset timeout on every key press (default for
|
||||
sequences).
|
||||
* `timeout`: Set the time window within which the match has to happen in ms.
|
||||
* `match_coord`: If True, matches key position in the matrix.
|
||||
|
||||
## Example Code
|
||||
```python
|
||||
from kmk.keys import KC, make_key
|
||||
from kmk.modules.combos import Combos, Chord, Sequence
|
||||
combos = Combos()
|
||||
keyboard.modules.append(combos)
|
||||
|
||||
make_key(
|
||||
names=('MYKEY',),
|
||||
on_press=lambda *args: print('I pressed MYKEY'),
|
||||
)
|
||||
|
||||
combos.combos = [
|
||||
Chord((KC.A, KC.B), KC.LSFT),
|
||||
Chord((KC.A, KC.B, KC.C), KC.LALT),
|
||||
Chord((0, 1), KC.ESC, match_coord=True),
|
||||
Chord((8, 9, 10), KC.MO(4), match_coord=True),
|
||||
Sequence((KC.LEADER, KC.A, KC.B), KC.C),
|
||||
Sequence((KC.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
|
||||
]
|
||||
```
|
84
docs/en/config_and_keymap.md
Normal file
84
docs/en/config_and_keymap.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Configuring KMK
|
||||
|
||||
KMK is configured through a rather large plain-old-Python class called
|
||||
`KMKKeyboard`. Subclasses of this configuration exist which pre-fill defaults
|
||||
for various known keyboards (for example, many QMK, TMK, or ZMK keyboards
|
||||
are supported with a nice!nano, or through our ItsyBitsy to Pro Micro pinout adapter.)
|
||||
This class is the main interface between end users and the inner workings of KMK.
|
||||
Let's dive in!
|
||||
|
||||
- Edit or create a file called `main.py` on your `CIRCUITPY` drive. You can also
|
||||
keep this file on your computer (perhaps under `user_keymaps` - please feel
|
||||
free to submit a pull request with your layout definitions!) and copy it over
|
||||
(either manually or, if you're adept with developer tooling and/or a command
|
||||
line, [our
|
||||
Makefile](https://github.com/KMKfw/kmk_firmware/blob/master/docs/flashing.md)).
|
||||
It's definitely recommended to keep a backup of your configuration somewhere
|
||||
that isn't the microcontroller itself - MCUs die, CircuitPython may run into
|
||||
corruption bugs, or you might just have bad luck and delete the wrong file
|
||||
some day.
|
||||
|
||||
- Assign a `KMKKeyboard` instance to a variable (ex. `keyboard = KMKKeyboard()` - note
|
||||
the parentheses)
|
||||
|
||||
- Make sure this `KMKKeyboard` instance is actually run at the end of the file with
|
||||
a block such as the following:
|
||||
|
||||
```python
|
||||
if __name__ == '__main__':
|
||||
keyboard.go()
|
||||
```
|
||||
|
||||
- Assign pins and your diode orientation (only necessary on handwire keyboards),
|
||||
for example:
|
||||
|
||||
```python
|
||||
import board
|
||||
|
||||
from kmk.scanners import DiodeOrientation
|
||||
|
||||
col_pins = (board.SCK, board.MOSI, board.MISO, board.RX, board.TX, board.D4)
|
||||
row_pins = (board.D10, board.D11, board.D12, board.D13, board.D9, board.D6, board.D5, board.SCL)
|
||||
rollover_cols_every_rows = 4
|
||||
diode_orientation = DiodeOrientation.COL2ROW
|
||||
```
|
||||
|
||||
The pins should be based on whatever CircuitPython calls pins on your particular
|
||||
board. You can find these in the REPL on your CircuitPython device:
|
||||
|
||||
```python
|
||||
import board
|
||||
print(dir(board))
|
||||
```
|
||||
|
||||
> Note: `rollover_cols_every_rows` is only supported with
|
||||
> `DiodeOrientation.COLUMNS`/`DiodeOrientation.COL2ROW`, not `DiodeOrientation.ROWS`/`DiodeOrientation.ROW2COL`. It is used for boards
|
||||
> such as the Planck Rev6 which reuse column pins to simulate a 4x12 matrix in
|
||||
> the form of an 8x6 matrix
|
||||
|
||||
- Import the global list of key definitions with `from kmk.keys import KC`. You
|
||||
can either print this out in the REPL as we did with `board` above, or simply
|
||||
look at [our Key
|
||||
documentation](https://github.com/KMKfw/kmk_firmware/blob/master/docs/keycodes.md).
|
||||
We've tried to keep that listing reasonably up to date, but if it feels like
|
||||
something is missing, you may need to read through `kmk/keys.py` (and then
|
||||
open a ticket to tell us our docs are out of date, or open a PR and fix the
|
||||
docs yourself!)
|
||||
|
||||
- Define a keymap, which is, in Python terms, a List of Lists of `Key` objects.
|
||||
A very simple keymap, for a keyboard with just two physical keys on a single
|
||||
layer, may look like this:
|
||||
|
||||
```python
|
||||
keyboard.keymap = [[KC.A, KC.B]]
|
||||
```
|
||||
|
||||
You can further define a bunch of other stuff:
|
||||
|
||||
- `keyboard.debug_enabled` which will spew a ton of debugging information to the serial
|
||||
console. This is very rarely needed, but can provide very valuable information
|
||||
if you need to open an issue.
|
||||
|
||||
- `keyboard.tap_time` which defines how long `KC.TT` and `KC.LT` will wait before
|
||||
considering a key "held" (see `layers.md`)
|
||||
|
68
docs/en/contributing.md
Normal file
68
docs/en/contributing.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Contributing
|
||||
KMK is a community effort and welcomes contributions of code and documentation from people
|
||||
of all backgrounds and levels of technical skill. As such, these guidelines should serve
|
||||
to make contributing as easy as possible for everyone while maintaining a consistent style.
|
||||
|
||||
## Contributing Code
|
||||
The following guidelines should ensure that any code contributed can be merged in as
|
||||
painlessly as possible. If you're unsure how to set up your development environment,
|
||||
feel free to join the chat, [#kmkfw:klar.sh on Matrix](https://matrix.to/#/#kmkfw:klar.sh).
|
||||
This channel is bridged to Discord [here](https://discord.gg/QBHUUpeGUd) for convenience.
|
||||
|
||||
### Code Style
|
||||
|
||||
KMK uses [Black](https://github.com/psf/black) with a Python 3.6 target and,
|
||||
[(controversially?)](https://github.com/psf/black/issues/594) single quotes.
|
||||
Further code styling is enforced with isort and flake8 with several plugins.
|
||||
|
||||
**NOTE:** before committing code, run `make fix-isort fix-formatting test` to
|
||||
reduce workload for the project's maintainers and prevent the CI pipeline from
|
||||
failing.
|
||||
|
||||
There are some limited exceptions to the code formatting rules, which can be
|
||||
found in `setup.cfg`, notably around `user_keymaps` (which are also not subject
|
||||
to Black formatting as documented in `pyproject.toml`)
|
||||
|
||||
### Tests
|
||||
|
||||
Unit tests within the `tests` folder mock various CircuitPython modules to allow
|
||||
them to be executed in a desktop development environment.
|
||||
|
||||
Execute tests using the command `python -m unittest`.
|
||||
|
||||
## Contributing Documentation
|
||||
While KMK welcomes documentation from anyone with and understanding of the issues
|
||||
and a willingness to write them up, it's a good idea to familiarize yourself with
|
||||
the docs. Documentation should be informative but concise.
|
||||
|
||||
### Styling
|
||||
Docs are written and rendered in GitHub Markdown. A comprehensive guide to GitHub's
|
||||
Markdown can be found [here](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
|
||||
|
||||
In particular, KMK's docs should include a title, demarcated with `#`, and subheadings
|
||||
should be demarcated with `##`, `###`, and so on. Headings should be short and specific.
|
||||
|
||||
### Example Code
|
||||
Where possible, practical code examples should be included in documentation to help
|
||||
new users understand how to implement features. In general, it's better to have a code-
|
||||
block with comments inside it rather than multiple blocks of code broken up with
|
||||
explanation.
|
||||
|
||||
Code blocks should be formatted as Python code as follows:
|
||||
````
|
||||
```python
|
||||
print('Hello, world!')
|
||||
```
|
||||
````
|
||||
|
||||
Inline code, indicated with `` `backticks` ``, should be used when calling out specific
|
||||
functions or Python keywords within the body of paragraphs or in lists.
|
||||
|
||||
## License, Copyright, and Legal
|
||||
|
||||
All software in this repository is licensed under the [GNU Public License,
|
||||
version 3](https://tldrlegal.com/license/gnu-general-public-license-v3-(gpl-3)).
|
||||
All documentation and hardware designs are licensed under the [Creative Commons
|
||||
Attribution-ShareAlike 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
license. Contributions to this repository must use these licenses unless
|
||||
otherwise agreed to by the Core team.
|
11
docs/en/debugging.md
Normal file
11
docs/en/debugging.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Debugging
|
||||
Debug will output most of the useful state to the console. This can be enable in your firmware
|
||||
by setting this in your keymap. NOTE that it will be slower, so only enable this when you
|
||||
need debugging.
|
||||
```python
|
||||
keyboard.debug_enabled = True
|
||||
```
|
||||
|
||||
The output can be viewed by connecting to the serial port of the keybord. Please refer to [THIS](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console) for
|
||||
more information when connecting to serial console. For Linux users, we recommend [picocom](https://github.com/npat-efault/picocom) or
|
||||
[screen](https://www.gnu.org/software/screen/manual/screen.html)
|
44
docs/en/dynamic_sequences.md
Normal file
44
docs/en/dynamic_sequences.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Dynamic Sequences
|
||||
Dynamic sequences allow you to record a sequence just by typing it without modifying your keymap. Any sequence recorded this way is temporary and will remain available until your keyboard reboots.
|
||||
|
||||
### How to use
|
||||
#### Recording
|
||||
1. Press record
|
||||
2. Type your sequence
|
||||
3. Press stop
|
||||
|
||||
#### Playing
|
||||
1. Press play to use your recorded sequence
|
||||
|
||||
## Enable dynamic sequences
|
||||
```python
|
||||
from kmk.modules.dynamic_sequences import DynamicSequences
|
||||
|
||||
keyboard.modules.append(DynamicSequences())
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|Key |Description |
|
||||
|-------------------------------|---------------------------------------|
|
||||
|`KC.RECORD_SEQUENCE()` |Start recording into the current slot |
|
||||
|`KC.PLAY_SEQUENCE()` |Play the sequence in the current slot |
|
||||
|`KC.STOP_SEQUENCE()` |Stop recording, playing, or configuring|
|
||||
|`KC.SET_SEQUENCE(x)` |Change to the sequence in slot `x` |
|
||||
|`KC.SET_SEQUENCE_REPETITIONS()`|Change to repepition config mode |
|
||||
|`KC.SET_SEQUENCE_INTERVAL()` |Change to interval config mode |
|
||||
|
||||
## Config
|
||||
```python
|
||||
dynamicSequences = DynamicSequences(
|
||||
slots=1, # The number of sequence slots to use
|
||||
timeout=60000, # Maximum time to spend in record or config mode before stopping automatically, milliseconds
|
||||
key_interval=0, # Milliseconds between key events while playing
|
||||
use_recorded_speed=False # Whether to play the sequence at the speed it was typed
|
||||
)
|
||||
```
|
||||
|
||||
## Sequence slots
|
||||
You can configure multiple slots that each store a different sequence. You can change to a specific slot with `KC.SET_SEQUENCE(x)`, where `x` is the sequence slot number (starting from `0`). Every keycode can take an optional number to change to a specific sequence slot before performing the action. For example `KC.PLAY_SEQUENCE(2)` will play the sequence in slot `2`. If a slot is not specified, the current slot will be used.
|
||||
|
||||
## Repeating sequences
|
||||
Sequences can be set to repeat automatically. The number of repetitions and the interval between repetitions can be set using `KC.SET_SEQUENCE_REPETITIONS()` and `KC.SET_SEQUENCE_INTERVAL()`. Using one of these keys will put the keyboard in sequence config mode. In this mode, keypresses will not be sent to the OS and you can use your number keys to type the number of repetitions or the interval time in seconds. This mode ends when you press `KC.ENTER`, `KC.STOP_SEQUENCE()`, or automatically when the timeout is reached. Repeat settings are stored in the current slot.
|
25
docs/en/easypoint.md
Normal file
25
docs/en/easypoint.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# AS5013 (aka 'EasyPoint')
|
||||
|
||||
Module handles the AM5013 Two-dimensional magnetic position sensor with digital coordinates output
|
||||
|
||||
Product page: https://www.mouser.dk/ProductDetail/ams/AS5013-IQFT?qs=abmNkq9no6D3ApA%252BrWSMNQ%3D%3D
|
||||
|
||||
### Usage
|
||||
|
||||
Declare I2C bus and add this module in your main class.
|
||||
|
||||
```python
|
||||
from kmk.modules.easypoint import Easypoint
|
||||
import busio
|
||||
|
||||
i2c = busio.I2C(scl=board.GP1, sda=board.GP0)
|
||||
|
||||
easypoint = Easypoint(i2c, address=0x40)
|
||||
keyboard.modules.append(easypoint)
|
||||
```
|
||||
|
||||
Further configuring the AS5013 involved x/y-offset, and deadzone.
|
||||
|
||||
```python
|
||||
easypoint = Easypoint(i2c, address=0x40, y_offset=Y_OFFSET, x_offset=X_OFFSET, dead_x=DEAD_X, dead_y=DEAD_Y)
|
||||
```
|
215
docs/en/encoder.md
Normal file
215
docs/en/encoder.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Encoder module
|
||||
Add twist control to your keyboard! Volume, zoom, anything you want.
|
||||
|
||||
I2C encoder type has been tested with the Adafruit I2C QT Rotary Encoder with NeoPixel.
|
||||
|
||||
**Note:** If you have a **split** keyboard and encoders on **both sides** should work, it's currently necessary to use the encoder-scanner explained at the bottom of [scanners docs](scanners.md).
|
||||
|
||||
## Enabling the extension
|
||||
The constructor(`EncoderHandler` class) takes a list of encoders, each one defined as either:
|
||||
|
||||
* a list of `pad_a` pin, `pad_b` pin, `button_pin` and optionally a flag set to `True` if you want encoder direction to be reversed;
|
||||
* a `busio.I2C`, address and optionally a flag set to `True` if you want it to be reversed.
|
||||
|
||||
The `encoder_map` is modeled after the keymap and works the same way. It should have as many layers (key pressed on "turned left", key pressed on "turned right", key pressed on "knob pressed") as your keymap, and use `KC.NO` keys for layers that you don't require any action. The encoder supports a velocity mode if you desire to make something for video or sound editing.
|
||||
|
||||
|
||||
|
||||
## How to use
|
||||
Here is all you need to use this module in your `main.py` / `code.py` file.
|
||||
|
||||
1. Load the module: import encoder handler and add it to keyboard modules.
|
||||
|
||||
```python
|
||||
from kmk.modules.encoder import EncoderHandler
|
||||
encoder_handler = EncoderHandler()
|
||||
keyboard.modules = [layers, modtap, encoder_handler]
|
||||
```
|
||||
|
||||
2. Define the pins for each encoder: `pin_a`, `pin_b` for rotations, `pin_button` for the switch in the encoder. Set switch to `None` if the encoder's button is handled differently (as a part of matrix for example) or not at all. If you want to invert the direction of the encoder, set the 4th (optional) parameter `is_inverted` to `True`. 5th parameter is [encoder divisor](#encoder-resolution) (optional), it can be either `2` or `4`.
|
||||
|
||||
```python
|
||||
# Regular GPIO Encoder
|
||||
encoder_handler.pins = (
|
||||
# regular direction encoder and a button
|
||||
(board.GP17, board.GP15, board.GP14,), # encoder #1
|
||||
# reversed direction encoder with no button handling and divisor of 2
|
||||
(board.GP3, board.GP5, None, True, 2,), # encoder #2
|
||||
)
|
||||
```
|
||||
|
||||
Or in case you have an I2C encoder on a special PCB (e.g. Adafruit I2C QT Rotary Encoder), define I2C encoder as following.
|
||||
|
||||
```python
|
||||
# I2C Encoder
|
||||
|
||||
# Setup i2c
|
||||
SDA = board.GP0
|
||||
SCL = board.GP1
|
||||
i2c = busio.I2C(SCL, SDA)
|
||||
|
||||
encoder_handler.pins = ((i2c, 0x36, False), (encoder 2 definition), etc. )
|
||||
```
|
||||
|
||||
3. Define the mapping of keys to be called for each layer.
|
||||
|
||||
```python
|
||||
# You can optionally predefine combo keys as for your layout
|
||||
Zoom_in = KC.LCTRL(KC.EQUAL)
|
||||
Zoom_out = KC.LCTRL(KC.MINUS)
|
||||
|
||||
encoder_handler.map = [ (( KC.VOLD, KC.VOLU, KC.MUTE), (encoder 2 definition), etc. ), # Layer 1
|
||||
((Zoom_out, Zoom_in, KC.NO), (encoder 2 definition), etc. ), # Layer 2
|
||||
((KC.A, KC.Z, KC.N1), (encoder 2 definition), etc. ), # Layer 3
|
||||
((KC.NO, KC.NO, KC.NO), (encoder 2 definition), etc. ), # Layer 4
|
||||
]
|
||||
```
|
||||
|
||||
## Encoder divisor
|
||||
|
||||
Depending on your encoder resolution, it may send 4 or 2 pulses (state changes) on every detent. This number is controlled by the `divisor` property. By default the divisor is set to 4, but if your encoder only activates on every second detent (skips pulses), set the divisor to 2. If the encoder activates twice on every detent, set the value to 4.
|
||||
|
||||
You can change the default globally for all encoders **before** initializing the encoder pins (`main.py` file):
|
||||
```python
|
||||
encoder_handler.divisor = 2
|
||||
encoder_handler.pins = ( (board.GP14, board.GP15, None), (board.GP26, board.GP27, None), )
|
||||
```
|
||||
|
||||
Or if you have different types of encoders, set divisor for each encoder individually:
|
||||
```python
|
||||
encoder_handler.pins = (
|
||||
(board.GP14, board.GP15, None, False, 4),
|
||||
(board.GP26, board.GP27, None, False, 2),
|
||||
(board.GP26, board.GP27, None ), # will be set to global default
|
||||
)
|
||||
```
|
||||
|
||||
This setting is equivalent to `divisor` in the [rotaryio](https://docs.circuitpython.org/en/latest/shared-bindings/rotaryio/index.html#rotaryio.IncrementalEncoder.divisor) module.
|
||||
The divisor of `1` for smooth encoders is not currently supported but you can use the divisor of `2` for them without issues and any noticeable difference.
|
||||
|
||||
## Handler methods overrides
|
||||
|
||||
Encoder methods `on_move_do` and `on_button_do` can be overridden for complex use cases.
|
||||
|
||||
## Connecting the encoder
|
||||
|
||||
Most EC11, EC12 and similar encoders have a common pinout shown below. For EVQWGD001 horizontal roller encoder, the pins are ordered `Pin A`, `Pin B`, `Ground`, and the fourth (furthest from the two switch pins) is not connected. This information is provided just for reference — always refer to the manufacturer datasheet for the correct pinout.
|
||||
|
||||
```
|
||||
+----------+
|
||||
Pin A ----| |
|
||||
| |----- Ground
|
||||
Ground ----| |
|
||||
| |----- Switch Pin
|
||||
Pin B ----| |
|
||||
+----------+
|
||||
```
|
||||
|
||||
In this configuration the encoder push button has its own dedicated pin. If the button should instead be a part of the matrix, it needs to be wired to Column and Row like other switches instead of ground.
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Full example (with 1 encoder)
|
||||
|
||||
```python
|
||||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.consts import UnicodeMode
|
||||
from kmk.keys import KC
|
||||
from kmk.scanners import DiodeOrientation
|
||||
from kmk.modules.layers import Layers
|
||||
from kmk.modules.encoder import EncoderHandler
|
||||
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
layers = Layers()
|
||||
encoder_handler = EncoderHandler()
|
||||
keyboard.modules = [layers, encoder_handler]
|
||||
|
||||
|
||||
keyboard.col_pins = (
|
||||
board.GP0, board.GP1, board.GP2, board.GP3, board.GP4, board.GP5,
|
||||
board.GP6, board.GP7, board.GP8, board.GP9, board.GP10, board.GP11,
|
||||
board.GP12, board.GP13,
|
||||
)
|
||||
keyboard.row_pins = (board.GP28, board.GP27, board.GP22, board.GP26, board.GP21)
|
||||
keyboard.diode_orientation = DiodeOrientation.COLUMNS
|
||||
|
||||
# I2C example
|
||||
#import busio
|
||||
#SDA = board.GP0
|
||||
#SCL = board.GP1
|
||||
#i2c = busio.I2C(SCL, SDA)
|
||||
#encoder_handler.i2c = ((i2c, 0x36, False),)
|
||||
|
||||
# encoder_handler.divisor = 2 # for encoders with more precision
|
||||
encoder_handler.pins = ((board.GP17, board.GP15, board.GP14, False),)
|
||||
|
||||
keyboard.tap_time = 250
|
||||
keyboard.debug_enabled = False
|
||||
|
||||
|
||||
# Filler keys
|
||||
_______ = KC.TRNS
|
||||
xxxxxxx = KC.NO
|
||||
tbdtbd = KC.A
|
||||
|
||||
|
||||
# Layers
|
||||
LYR_STD, LYR_EXT, LYR_NUM, LYR_GAME = 0, 1, 2, 3
|
||||
|
||||
TO_STD = KC.DF(LYR_STD)
|
||||
MT_EXT = KC.MO(LYR_EXT)
|
||||
TO_NUM = KC.MO(LYR_NUM)
|
||||
TO_GAME = KC.DF(LYR_GAME)
|
||||
|
||||
|
||||
# Keymap
|
||||
|
||||
keyboard.keymap = [
|
||||
# Standard (ISO) Layer
|
||||
[
|
||||
KC.ESC , KC.N1 , KC.N2 , KC.N3 , KC.N4 , KC.N5 , KC.N6 , KC.N7 , KC.N8 , KC.N9 , KC.N0 , KC.MINS, KC.EQL , KC.BSPC,
|
||||
KC.TAB , KC.Q , KC.W , KC.E , KC.R , KC.T , KC.Y , KC.U , KC.I , KC.O , KC.P , KC.LBRC, KC.RBRC, KC.DEL ,
|
||||
xxxxxxx, KC.A , KC.S , KC.D , KC.F , KC.G , KC.H , KC.J , KC.K , KC.L , KC.SCLN, KC.QUOT, KC.NUHS, xxxxxxx,
|
||||
KC.LSFT, KC.NUBS, KC.Z , KC.X , KC.C , KC.V , KC.B , KC.N , KC.M , KC.COMM, KC.DOT , KC.SLSH, KC.UP , KC.ENT ,
|
||||
KC.LCTL, KC.LGUI, xxxxxxx, KC.LALT, MT_EXT , xxxxxxx, KC.SPC , xxxxxxx, KC.RALT, TO_NUM , KC.RSFT, KC.LEFT, KC.DOWN, KC.RGHT,
|
||||
],
|
||||
# Extra Keys Layer
|
||||
[
|
||||
TO_STD , KC.F1 , KC.F2 , KC.F3 , KC.F4 , KC.F5 , KC.F6 , KC.F7 , KC.F8 , KC.F9 , KC.F10 , KC.F11 , KC.F12 , KC.RESET,
|
||||
_______, KC.N1 , KC.N2 , KC.N3 , KC.N4 , KC.N5 , KC.N6 , KC.N7 , KC.N8 , KC.N9 , KC.N0 , KC.MINS, KC.EQL , _______,
|
||||
xxxxxxx, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, xxxxxxx,
|
||||
KC.LSFT, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC.PGUP, _______,
|
||||
KC.LCTL, KC.LGUI, xxxxxxx, KC.LALT, MT_EXT , xxxxxxx, _______, xxxxxxx, _______, TO_NUM , _______, KC.HOME, KC.PGDN, KC.END ,
|
||||
],
|
||||
# NumPad Layer
|
||||
[
|
||||
TO_STD , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, KC.P7 , KC.P8 , KC.P9 , KC.PSLS, xxxxxxx, xxxxxxx, KC.BSPC,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, KC.P4 , KC.P5 , KC.P6 , KC.PAST, xxxxxxx, xxxxxxx, KC.DEL ,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, KC.LPRN, KC.P1 , KC.P2 , KC.P3 , KC.PPLS, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, KC.RPRN, KC.P0 , KC.PDOT, _______, KC.PMNS, xxxxxxx, xxxxxxx, KC.PENT,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, MT_EXT , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, TO_NUM , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
],
|
||||
# Gaming Layer
|
||||
[
|
||||
TO_STD , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, MT_EXT , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx, TO_NUM , xxxxxxx, xxxxxxx, xxxxxxx, xxxxxxx,
|
||||
],
|
||||
]
|
||||
|
||||
# Rotary Encoder (1 encoder / 1 definition per layer)
|
||||
encoder_handler.map = [ ((KC.UP, KC.DOWN, KC.MUTE),), # Standard
|
||||
((KC.VOLD, KC.VOLU, KC.MUTE),), # Extra
|
||||
((KC.A, KC.Z, KC.N1),), # NumPad not yet properly configured
|
||||
((KC.A, KC.Z, KC.N1),), # Gaming not yet properly configured
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
keyboard.go()
|
||||
```
|
47
docs/en/extension_statusled.md
Normal file
47
docs/en/extension_statusled.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Status LEDs
|
||||
|
||||
Indicate which layer you are on with an array of single leds.
|
||||
|
||||
During startup the leds light up to indicate that the bootup is finished.
|
||||
|
||||
For the time being just a simple consecutive single led
|
||||
indicator. And when there are more layers than leds it
|
||||
wraps around to the first led again.
|
||||
(Also works for a single led, which just lights when any
|
||||
layer is active)
|
||||
|
||||
_Most of the code comes from the Mono color LED backlight extension_.
|
||||
|
||||
## Enabling the extension
|
||||
|
||||
To enable the extension you need to define a list of `led_pins`. It can be a list of a one, two or three pins.
|
||||
|
||||
```python
|
||||
from kmk.extensions.statusled import statusLED
|
||||
import board
|
||||
|
||||
statusLED = statusLED(led_pins=[board.GP0, board.GP1, board.GP2])
|
||||
keyboard.extensions.append(statusLED)
|
||||
```
|
||||
|
||||
## [Keycodes]
|
||||
|
||||
| Key | Aliases | Description |
|
||||
| ------------- | ------- | ------------------- |
|
||||
| `KC.SLED_INC` | | Increase Brightness |
|
||||
| `KC.SLED_DEC` | | Decrease Brightness |
|
||||
|
||||
## Configuration
|
||||
|
||||
All of these values can be set by default for when the keyboard boots.
|
||||
|
||||
```python
|
||||
statusLED = statusLED(
|
||||
led_pin=led_pin,
|
||||
brightness=30,
|
||||
brightness_step=5,
|
||||
brightness_limit=100,
|
||||
)
|
||||
```
|
||||
|
||||
The brightness values are in percentages.
|
29
docs/en/extension_stringy_keymaps.md
Normal file
29
docs/en/extension_stringy_keymaps.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Stringy Keymaps
|
||||
|
||||
Enables referring to keys by `'NAME'` rather than `KC.NAME`.
|
||||
|
||||
For example:
|
||||
|
||||
```python
|
||||
from kmk.extensions.stringy_keymaps import StringyKeymaps
|
||||
|
||||
# Normal
|
||||
# keyboard.keymap = [[ KC.A, KC.B, KC.RESET ]]
|
||||
|
||||
# Indexed
|
||||
# keyboard.keymap = [[ KC['A'], KC['B'], KC['RESET'] ]]
|
||||
|
||||
# String names
|
||||
keyboard.keymap = [[ 'A' , 'B', 'RESET' ]]
|
||||
|
||||
stringyKeymaps = StringyKeymaps()
|
||||
|
||||
# Enabling debug will show each replacement or failure.
|
||||
# This is recommended during the initial development of a keyboard.
|
||||
# stringyKeymaps.debug_enable = True
|
||||
|
||||
keyboard.extensions.append(stringyKeymaps)
|
||||
```
|
||||
|
||||
It should be noted that these are **not** ASCII. The string is **not** what
|
||||
will be sent to the computer. The examples above have no functional difference.
|
19
docs/en/extensions.md
Normal file
19
docs/en/extensions.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Extensions
|
||||
|
||||
Extensions add features that change the experience, but not the core features of
|
||||
the keyboard. They are meant to be easy to add, and create your own. These live in
|
||||
a sandbox to help prevent any bad code from crashing your keyboard.
|
||||
|
||||
## Core Extensions
|
||||
|
||||
These extensions are provided in all builds and can be enabled. Currently offered
|
||||
extensions are
|
||||
|
||||
- [International](international.md): Adds international keycodes
|
||||
- [LED](led.md): Adds backlight support. This is for monocolor backlight, not RGB
|
||||
- [LockStatus](lock_status.md): Exposes host-side locks like caps or num lock.
|
||||
- [MediaKeys](media_keys.md): Adds support for media keys such as volume
|
||||
- [RGB](rgb.md): RGB lighting for underglow. Will work on most matrix RGB as will
|
||||
be treated the same as underglow.
|
||||
- [Status LED](extension_statusled.md): Indicates which layer you are on with an array of single leds.
|
||||
- [Stringy Keymaps](extension_stringy_keymaps): Enables referring to keys by `'NAME'` rather than `KC.NAME`
|
38
docs/en/flashing.md
Normal file
38
docs/en/flashing.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# Flashing Instructions
|
||||
|
||||
In general, we recommend using the instructions in `README.md`, however, mostly
|
||||
as a development artifact, another method of flashing KMK exists (tested and
|
||||
supported only on Linux, though it should also work on macOS, the BSDs, and
|
||||
other Unix-likes. It may also work on Cygwin and the Windows Subsystem for
|
||||
Linux).
|
||||
|
||||
Given `make` and `rsync` are available on your system (in `$PATH`), the
|
||||
following will copy the `kmk` tree to your CircuitPython device, and will copy
|
||||
the file defined as `USER_KEYMAP` as your `main.py`. It will also copy our
|
||||
`boot.py`, which allocates a larger stack size (simply - more of the device's
|
||||
RAM will be available to KMK and your keyboard config) than CircuitPython's
|
||||
default. If any of these files exist on your CircuitPython device already, they
|
||||
will be overwritten without a prompt.
|
||||
|
||||
If you get permissions errors here, **don't run make as root or with sudo**. See
|
||||
`Troubleshooting` below.
|
||||
|
||||
```sh
|
||||
make MOUNTPOINT=/media/CIRCUITPY USER_KEYMAP=user_keymaps/nameofyourkeymap.py BOARD=board/nameofyourboard/kb.py
|
||||
```
|
||||
|
||||
# Troubleshooting
|
||||
## Linux/BSD
|
||||
|
||||
Check to see if your drive may have mounted elsewhere with a GUI tool or other
|
||||
automounter. Most of these tools will mount your device under `/media`, probably
|
||||
as `/media/CIRCUITPY`. If it's not mounted, you can read up on how to mount a
|
||||
drive manually
|
||||
[here](https://wiki.archlinux.org/index.php/File_systems#Mount_a_file_system).
|
||||
|
||||
For example,
|
||||
|
||||
`sudo mount -o uid=$(id -u),gid=$(id -g) /dev/disk/by-label/CIRCUITPY ~/mnt`
|
||||
|
||||
If you're still having issues, check out our support page to see where you can
|
||||
come say hi and the community will gladly help you out.
|
6
docs/en/handwiring.md
Normal file
6
docs/en/handwiring.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Handwire keyboards
|
||||
This guide will not talk about the physical wiring. Check out our
|
||||
[recommended microcontrollers](Officially_Supported_Microcontrollers.md) and
|
||||
follow the amazing guide for that [here](https://docs.qmk.fm/#/hand_wire). That
|
||||
guide can be followed until you are setting up the firmware. After wiring the
|
||||
keyboard, you can refer to our porting guide [here](porting_to_kmk.md)
|
35
docs/en/international.md
Normal file
35
docs/en/international.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# International Keycodes
|
||||
International extension adds keys for non US layouts. It can simply be added to
|
||||
the extensions list.
|
||||
|
||||
```python
|
||||
from kmk.extensions.international import International
|
||||
keyboard.extensions.append(International())
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------|--------------------|-----------------------------------------------|
|
||||
|`KC.NONUS_HASH` |`KC.NUHS` |Non-US `#` and `~` |
|
||||
|`KC.NONUS_BSLASH` |`KC.NUBS` |Non-US `\` and <code>|</code> |
|
||||
|`KC.INT1` |`KC.RO` |JIS `\` and <code>|</code> |
|
||||
|`KC.INT2` |`KC.KANA` |JIS Katakana/Hiragana |
|
||||
|`KC.INT3` |`KC.JYEN` |JIS `¥` |
|
||||
|`KC.INT4` |`KC.HENK` |JIS Henkan |
|
||||
|`KC.INT5` |`KC.MHEN` |JIS Muhenkan |
|
||||
|`KC.INT6` | |JIS Numpad `,` |
|
||||
|`KC.INT7` | |International 7 |
|
||||
|`KC.INT8` | |International 8 |
|
||||
|`KC.INT9` | |International 9 |
|
||||
|`KC.LANG1` |`KC.HAEN` |Hangul/English |
|
||||
|`KC.LANG2` |`KC.HANJ` |Hanja |
|
||||
|`KC.LANG3` | |JIS Katakana |
|
||||
|`KC.LANG4` | |JIS Hiragana |
|
||||
|`KC.LANG5` | |JIS Zenkaku/Hankaku |
|
||||
|`KC.LANG6` | |Language 6 |
|
||||
|`KC.LANG7` | |Language 7 |
|
||||
|`KC.LANG8` | |Language 8 |
|
||||
|`KC.LANG9` | |Language 9 |
|
||||
|
||||
|
226
docs/en/keycodes.md
Normal file
226
docs/en/keycodes.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# Keys Overview
|
||||
|
||||
## [Basic Keys]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------|--------------------|-----------------------------------------------|
|
||||
|`KC.NO` | |Ignore this key (NOOP) |
|
||||
|`KC.TRANSPARENT` |`KC.TRNS` |Use the next lowest non-transparent key |
|
||||
|`KC.A` | |`a` and `A` |
|
||||
|`KC.B` | |`b` and `B` |
|
||||
|`KC.C` | |`c` and `C` |
|
||||
|`KC.D` | |`d` and `D` |
|
||||
|`KC.E` | |`e` and `E` |
|
||||
|`KC.F` | |`f` and `F` |
|
||||
|`KC.G` | |`g` and `G` |
|
||||
|`KC.H` | |`h` and `H` |
|
||||
|`KC.I` | |`i` and `I` |
|
||||
|`KC.J` | |`j` and `J` |
|
||||
|`KC.K` | |`k` and `K` |
|
||||
|`KC.L` | |`l` and `L` |
|
||||
|`KC.M` | |`m` and `M` |
|
||||
|`KC.N` | |`n` and `N` |
|
||||
|`KC.O` | |`o` and `O` |
|
||||
|`KC.P` | |`p` and `P` |
|
||||
|`KC.Q` | |`q` and `Q` |
|
||||
|`KC.R` | |`r` and `R` |
|
||||
|`KC.S` | |`s` and `S` |
|
||||
|`KC.T` | |`t` and `T` |
|
||||
|`KC.U` | |`u` and `U` |
|
||||
|`KC.V` | |`v` and `V` |
|
||||
|`KC.W` | |`w` and `W` |
|
||||
|`KC.X` | |`x` and `X` |
|
||||
|`KC.Y` | |`y` and `Y` |
|
||||
|`KC.Z` | |`z` and `Z` |
|
||||
|`KC.N1` | |`1` and `!` |
|
||||
|`KC.N2` | |`2` and `@` |
|
||||
|`KC.N3` | |`3` and `#` |
|
||||
|`KC.N4` | |`4` and `$` |
|
||||
|`KC.N5` | |`5` and `%` |
|
||||
|`KC.N6` | |`6` and `^` |
|
||||
|`KC.N7` | |`7` and `&` |
|
||||
|`KC.N8` | |`8` and `*` |
|
||||
|`KC.N9` | |`9` and `(` |
|
||||
|`KC.N0` | |`0` and `)` |
|
||||
|`KC.ENTER` |`KC.ENT` |Return (Enter) |
|
||||
|`KC.ESCAPE` |`KC.ESC` |Escape |
|
||||
|`KC.BSPACE` |`KC.BSPC` |Delete (Backspace) |
|
||||
|`KC.TAB` | |Tab |
|
||||
|`KC.SPACE` |`KC.SPC` |Spacebar |
|
||||
|`KC.MINUS` |`KC.MINS` |`-` and `_` |
|
||||
|`KC.EQUAL` |`KC.EQL` |`=` and `+` |
|
||||
|`KC.LBRACKET` |`KC.LBRC` |`[` and `{` |
|
||||
|`KC.RBRACKET` |`KC.RBRC` |`]` and `}` |
|
||||
|`KC.BSLASH` |`KC.BSLS` |`\` and <code>|</code> |
|
||||
|`KC.SCOLON` |`KC.SCLN` |`;` and `:` |
|
||||
|`KC.QUOTE` |`KC.QUOT` |`'` and `"` |
|
||||
|`KC.GRAVE` |`KC.GRV`, `KC.ZKHK` |<code>`</code> and `~`, JIS Zenkaku/Hankaku|
|
||||
|`KC.COMMA` |`KC.COMM` |`,` and `<` |
|
||||
|`KC.DOT` | |`.` and `>` |
|
||||
|`KC.SLASH` |`KC.SLSH` |`/` and `?` |
|
||||
|`KC.CAPSLOCK` |`KC.CLCK`, `KC.CAPS`|Caps Lock |
|
||||
|`KC.F1` | |F1 |
|
||||
|`KC.F2` | |F2 |
|
||||
|`KC.F3` | |F3 |
|
||||
|`KC.F4` | |F4 |
|
||||
|`KC.F5` | |F5 |
|
||||
|`KC.F6` | |F6 |
|
||||
|`KC.F7` | |F7 |
|
||||
|`KC.F8` | |F8 |
|
||||
|`KC.F9` | |F9 |
|
||||
|`KC.F10` | |F10 |
|
||||
|`KC.F11` | |F11 |
|
||||
|`KC.F12` | |F12 |
|
||||
|`KC.PSCREEN` |`KC.PSCR` |Print Screen |
|
||||
|`KC.SCROLLLOCK` |`KC.SLCK` |Scroll Lock |
|
||||
|`KC.PAUSE` |`KC.PAUS`, `KC.BRK` |Pause |
|
||||
|`KC.INSERT` |`KC.INS` |Insert |
|
||||
|`KC.HOME` | |Home |
|
||||
|`KC.PGUP` | |Page Up |
|
||||
|`KC.DELETE` |`KC.DEL` |Forward Delete |
|
||||
|`KC.END` | |End |
|
||||
|`KC.PGDOWN` |`KC.PGDN` |Page Down |
|
||||
|`KC.RIGHT` |`KC.RGHT` |Right Arrow |
|
||||
|`KC.LEFT` | |Left Arrow |
|
||||
|`KC.DOWN` | |Down Arrow |
|
||||
|`KC.UP` | |Up Arrow |
|
||||
|`KC.NUMLOCK` |`KC.NLCK` |Keypad Num Lock and Clear |
|
||||
|`KC.KP_SLASH` |`KC.PSLS` |Keypad `/` |
|
||||
|`KC.KP_ASTERISK` |`KC.PAST` |Keypad `*` |
|
||||
|`KC.KP_MINUS` |`KC.PMNS` |Keypad `-` |
|
||||
|`KC.KP_PLUS` |`KC.PPLS` |Keypad `+` |
|
||||
|`KC.KP_ENTER` |`KC.PENT` |Keypad Enter |
|
||||
|`KC.KP_1` |`KC.P1` |Keypad `1` and End |
|
||||
|`KC.KP_2` |`KC.P2` |Keypad `2` and Down Arrow |
|
||||
|`KC.KP_3` |`KC.P3` |Keypad `3` and Page Down |
|
||||
|`KC.KP_4` |`KC.P4` |Keypad `4` and Left Arrow |
|
||||
|`KC.KP_5` |`KC.P5` |Keypad `5` |
|
||||
|`KC.KP_6` |`KC.P6` |Keypad `6` and Right Arrow |
|
||||
|`KC.KP_7` |`KC.P7` |Keypad `7` and Home |
|
||||
|`KC.KP_8` |`KC.P8` |Keypad `8` and Up Arrow |
|
||||
|`KC.KP_9` |`KC.P9` |Keypad `9` and Page Up |
|
||||
|`KC.KP_0` |`KC.P0` |Keypad `0` and Insert |
|
||||
|`KC.KP_DOT` |`KC.PDOT` |Keypad `.` and Delete |
|
||||
|`KC.KP_EQUAL` |`KC.PEQL` |Keypad `=` |
|
||||
|`KC.F13` | |F13 |
|
||||
|`KC.F14` | |F14 |
|
||||
|`KC.F15` | |F15 |
|
||||
|`KC.F16` | |F16 |
|
||||
|`KC.F17` | |F17 |
|
||||
|`KC.F18` | |F18 |
|
||||
|`KC.F19` | |F19 |
|
||||
|`KC.F20` | |F20 |
|
||||
|`KC.F21` | |F21 |
|
||||
|`KC.F22` | |F22 |
|
||||
|`KC.F23` | |F23 |
|
||||
|`KC.F24` | |F24 |
|
||||
|`KC.LOCKING_CAPS` |`KC.LCAP` |Locking Caps Lock |
|
||||
|`KC.LOCKING_NUM` |`KC.LNUM` |Locking Num Lock |
|
||||
|`KC.LOCKING_SCROLL` |`KC.LSCR` |Locking Scroll Lock |
|
||||
|`KC.KP_COMMA` |`KC.PCMM` |Keypad `,` |
|
||||
|`KC.KP_EQUAL_AS400` | |Keypad `=` on AS/400 keyboards |
|
||||
|`KC.LCTRL` |`KC.LCTL` |Left Control |
|
||||
|`KC.LSHIFT` |`KC.LSFT` |Left Shift |
|
||||
|`KC.LALT` | |Left Alt |
|
||||
|`KC.LGUI` |`KC.LCMD`, `KC.LWIN`|Left GUI (Windows/Command/Meta key) |
|
||||
|`KC.RCTRL` |`KC.RCTL` |Right Control |
|
||||
|`KC.RSHIFT` |`KC.RSFT` |Right Shift |
|
||||
|`KC.RALT` | |Right Alt |
|
||||
|`KC.RGUI` |`KC.RCMD`, `KC.RWIN`|Right GUI (Windows/Command/Meta key) |
|
||||
|
||||
|
||||
## [US ANSI Shifted Symbols]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|------------------------|-------------------|-------------------|
|
||||
|`KC.TILDE` |`KC.TILD` |`~` |
|
||||
|`KC.EXCLAIM` |`KC.EXLM` |`!` |
|
||||
|`KC.AT` | |`@` |
|
||||
|`KC.HASH` | |`#` |
|
||||
|`KC.DOLLAR` |`KC.DLR` |`$` |
|
||||
|`KC.PERCENT` |`KC.PERC` |`%` |
|
||||
|`KC.CIRCUMFLEX` |`KC.CIRC` |`^` |
|
||||
|`KC.AMPERSAND` |`KC.AMPR` |`&` |
|
||||
|`KC.ASTERISK` |`KC.ASTR` |`*` |
|
||||
|`KC.LEFT_PAREN` |`KC.LPRN` |`(` |
|
||||
|`KC.RIGHT_PAREN` |`KC.RPRN` |`)` |
|
||||
|`KC.UNDERSCORE` |`KC.UNDS` |`_` |
|
||||
|`KC.PLUS` | |`+` |
|
||||
|`KC.LEFT_CURLY_BRACE` |`KC.LCBR` |`{` |
|
||||
|`KC.RIGHT_CURLY_BRACE` |`KC.RCBR` |`}` |
|
||||
|`KC.PIPE` | |<code>|</code>|
|
||||
|`KC.COLON` |`KC.COLN` |`:` |
|
||||
|`KC.DOUBLE_QUOTE` |`KC.DQUO`, `KC.DQT`|`"` |
|
||||
|`KC.LEFT_ANGLE_BRACKET` |`KC.LABK`, `KC.LT` |`<` |
|
||||
|`KC.RIGHT_ANGLE_BRACKET`|`KC.RABK`, `KC.GT` |`>` |
|
||||
|`KC.QUESTION` |`KC.QUES` |`?` |
|
||||
|
||||
## [International Keys]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|------------------------|------------------------------|-----------------------|
|
||||
|`KC.NONUS_HASH` |`KC.NUHS` |ISO Left of Return |
|
||||
|`KC.NONUS_BSLASH` |`KC.NUBS` |ISO Right of LSHIFT |
|
||||
|`KC.APPLICATION` |`KC.APP`,`KC.SEL`,`KC.WINMENU`|Menu Key (Near RCTRL) |
|
||||
|`KC.INT1` |`KC.RO` | |
|
||||
|`KC.INT2` |`KC.KANA` | |
|
||||
|`KC.INT3` |`KC.JYEN` | |
|
||||
|`KC.INT4` |`KC.HENK` | |
|
||||
|`KC.INT5` |`KC.MHEN` | |
|
||||
|`KC.INT6` | | |
|
||||
|`KC.INT7` | | |
|
||||
|`KC.INT8` | | |
|
||||
|`KC.INT9` | | |
|
||||
|`KC.LANG1` |`HAEN` | |
|
||||
|`KC.LANG2` |`HAEJ` | |
|
||||
|`KC.LANG3` | | |
|
||||
|`KC.LANG4` | | |
|
||||
|`KC.LANG5` | | |
|
||||
|`KC.LANG6` | | |
|
||||
|`KC.LANG7` | | |
|
||||
|`KC.LANG8` | | |
|
||||
|`KC.LANG9` | | |
|
||||
|
||||
## [Internal Keys]
|
||||
|
||||
|Key |Description |
|
||||
|-----------------------|---------------------------------------------------------------------|
|
||||
|`KC.RESET` |Restarts the keyboard |
|
||||
|`KC.RELOAD`, `KC.RLD` |Reloads the keyboard software, preserving any serial connections |
|
||||
|`KC.DEBUG` |Toggle `debug_enabled`, which enables log spew to serial console |
|
||||
|`KC.GESC` |Escape when tapped, <code>`</code> when pressed with Shift or GUI|
|
||||
|`KC.BKDL` |Backspace when tapped, Delete when pressed with GUI |
|
||||
|`KC.UC_MODE_NOOP` |Sets UnicodeMode to NOOP |
|
||||
|`KC.UC_MODE_LINUX` |Sets UnicodeMode to Linux |
|
||||
|`KC.UC_MODE_MACOS` |Sets UnicodeMode to macOS |
|
||||
|`KC.UC_MODE_WINC` |Sets UnicodeMode to WinCompose |
|
||||
|`KC.MACRO_SLEEP_MS(ms)`|Sleeps in a macro. See [SEQUENCES](/docs/sequences.md) for more information. |
|
||||
|
||||
|
||||
## [Modifiers]
|
||||
|
||||
|Key |Description |
|
||||
|-------------|----------------------------------------------------|
|
||||
|`KC.HYPR` |Hold Left Control, Shift, Alt and GUI |
|
||||
|`KC.MEH` |Hold Left Control, Shift and Alt |
|
||||
|`KC.LCTL(kc)`|Hold Left Control and press `kc` |
|
||||
|`KC.LSFT(kc)`|Hold Left Shift and press `kc` |
|
||||
|`KC.LALT(kc)`|Hold Left Alt and press `kc` |
|
||||
|`KC.LGUI(kc)`|Hold Left GUI and press `kc` |
|
||||
|`KC.RCTL(kc)`|Hold Right Control and press `kc` |
|
||||
|`KC.RSFT(kc)`|Hold Right Shift and press `kc` |
|
||||
|`KC.RALT(kc)`|Hold Right Alt and press `kc` |
|
||||
|`KC.RGUI(kc)`|Hold Right GUI and press `kc` |
|
||||
|
||||
|
||||
## [Bluetooth Keys]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------------|-------------------|----------------------------------|
|
||||
|`KC.BT_CLEAR_BONDS` |`KC.BT_CLR` |Clears all stored bondings |
|
||||
|`KC.BT_NEXT_CONN` |`KC.BT_NXT` |Selects the next BT connection |
|
||||
|`KC.BT_PREV_CONN` |`KC.BT_PRV` |Selects the previous BT connection|
|
||||
|
||||
|
||||
## [Media Keys]
|
||||
See [MediaKeys extension](media_keys.md).
|
187
docs/en/keys.md
Normal file
187
docs/en/keys.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Keys
|
||||
|
||||
> NOTE: This is not a lookup table of key objects provided by KMK. That listing
|
||||
> can be found in [`keycodes.md`](/docs/keycodes.md). It's probably worth a look at the raw source if
|
||||
> you're stumped: [`kmk/keys.py`](/kmk/keys.py).
|
||||
|
||||
This is a bunch of documentation about how a physical keypress translates to
|
||||
events (and the lifecycle of said events) in KMK. It's somewhat technical, but
|
||||
if you're looking to extend your keyboard's functionality with extra code,
|
||||
you'll need at least some of this technical knowledge.
|
||||
|
||||
The first few steps in the process aren't all that interesting for most
|
||||
workflows, which is why they're buried deep in KMK: we scan a bunch of GPIO
|
||||
lanes (about as quickly as CircuitPython will let us) to see where, in a matrix
|
||||
of keys, a key has been pressed. The technical details about this process [are
|
||||
probably best left to
|
||||
Wikipedia](https://en.wikipedia.org/wiki/Keyboard_matrix_circuit). Then, we scan
|
||||
through the defined keymap, finding the first valid key at this index based on
|
||||
the stack of currently active layers (this logic, if you want to read through
|
||||
the code, is in [`kmk/kmk_keyboard.py`](/kmk/kmk_keyboard.py), method `_find_key_in_map`).
|
||||
|
||||
The next few steps are the interesting part, but to understand them, we need to
|
||||
understand a bit about what a `Key` object is (found in [`kmk/keys.py`](/kmk/keys.py)). `Key`
|
||||
objects have a few core pieces of information:
|
||||
|
||||
- Their `code`, which can be any integer. Integers below
|
||||
`FIRST_KMK_INTERNAL_KEY` are sent through to the HID stack (and thus the
|
||||
computer, which will translate that integer to something meaningful - for
|
||||
example, `code=4` becomes `a` on a US QWERTY/Dvorak keyboard).
|
||||
|
||||
- Their attached modifiers (to implement things like shifted keys or `KC.HYPR`,
|
||||
which are single key presses sending along more than one key in a single HID
|
||||
report. This is a distinct concept from Sequences, which are a KMK feature
|
||||
documented in [`sequences.md`](/docs/sequences.md)). For almost all purposes outside of KMK core,
|
||||
this field should be ignored - it can be safely populated through far more
|
||||
sane means than futzing with it by hand.
|
||||
|
||||
- Some data on whether the key should actually be pressed or released - this is
|
||||
mostly an implementation detail of how Sequences work, where, for example,
|
||||
`KC.RALT` may need to be held down for the entirety of a sequence, rather than
|
||||
being released immediately before moving to the next character. Usually end
|
||||
users shouldn't need to mess with this, but the fields are called `no_press`
|
||||
and `no_release` and are referenced in a few places in the codebase if you
|
||||
need examples.
|
||||
|
||||
- Handlers for "press" (sometimes known as "keydown") and "release" (sometimes
|
||||
known as "keyup") events. KMK provides handlers for standard keyboard
|
||||
functions and some special override keys (like `KC.GESC`, which is an enhanced
|
||||
form of existing ANSI keys) in [`kmk/handlers/stock.py`](/kmk/handlers/stock.py), for layer switching in
|
||||
[`kmk/modules/layers.py`](/kmk/modules/layers.py), and for everything related to Sequences (see
|
||||
[`sequences.md`](/docs/sequences.md) again) in [`kmk/handlers/sequences.py`](/kmk/handlers/sequences.py). We'll discuss these more
|
||||
shortly.
|
||||
|
||||
- Optional callbacks to be run before and/or after the above handlers. More on
|
||||
that soon.
|
||||
|
||||
- A generic `meta` field, which is most commonly used for "argumented" keys -
|
||||
objects in the `KC` object which are actually functions that return `Key`
|
||||
instances, which often need to access the arguments passed into the "outer"
|
||||
function. Many of these examples are related to layer switching - for example,
|
||||
`KC.MO` is implemented as an argumented key - when the user adds `KC.MO(1)` to
|
||||
their keymap, the function call returns a `Key` object with `meta` set to an
|
||||
object containing `layer` and `kc` properties, for example. There's other uses
|
||||
for `meta`, and examples can be found in [`kmk/types.py`](/kmk/types.py)
|
||||
|
||||
`Key` objects can also be chained together by calling them! To create a key
|
||||
which holds Control and Shift simultaneously, we can simply do:
|
||||
|
||||
```python
|
||||
CTRLSHFT = KC.LCTL(KC.LSFT)
|
||||
|
||||
keyboard.keymap = [ ... CTRLSHFT ... ]
|
||||
```
|
||||
|
||||
When a key is pressed and we've pulled a `Key` object out of the keymap, the
|
||||
following will happen:
|
||||
|
||||
- Pre-press callbacks will be run in the order they were assigned, with their
|
||||
return values discarded (unless the user attached these, they will almost
|
||||
never exist)
|
||||
- The assigned press handler will be run (most commonly, this is provided by
|
||||
KMK)
|
||||
- Post-press callbacks will be run in the order they were assigned, with their
|
||||
return values discarded (unless the user attached these, they will almost
|
||||
never exist)
|
||||
|
||||
These same steps are run for when a key is released.
|
||||
|
||||
_So now... what's a handler, and what's a pre/post callback?!_
|
||||
|
||||
All of these serve roughly the same purpose: to _do something_ with the key's
|
||||
data, or to fire off side effects. Most handlers are provided by KMK internally
|
||||
and modify the `InternalState` in some way - adding the key to the HID queue,
|
||||
changing layers, etc. The pre/post handlers are designed to allow functionality
|
||||
to be bolted on at these points in the event flow without having to reimplement
|
||||
(or import and manually call) the internal handlers.
|
||||
|
||||
All of these methods take the same arguments, and for this, I'll lift a
|
||||
docstring straight out of the source:
|
||||
|
||||
> Receives the following:
|
||||
>
|
||||
> - self (this Key instance)
|
||||
> - state (the current InternalState)
|
||||
> - KC (the global KC lookup table, for convenience)
|
||||
> - `coord_int` (an internal integer representation of the matrix coordinate
|
||||
> for the pressed key - this is likely not useful to end users, but is
|
||||
> provided for consistency with the internal handlers)
|
||||
> - `coord_raw` (an X,Y tuple of the matrix coordinate - also likely not useful)
|
||||
>
|
||||
> The return value of the provided callback is discarded. Exceptions are _not_
|
||||
> caught, and will likely crash KMK if not handled within your function.
|
||||
>
|
||||
> These handlers are run in attachment order: handlers provided by earlier
|
||||
> calls of this method will be executed before those provided by later calls.
|
||||
|
||||
This means if you want to add things like underglow/LED support, or have a
|
||||
button that triggers your GSM modem to call someone, or whatever else you can
|
||||
hack up in CircuitPython, which also retaining layer-switching abilities or
|
||||
whatever the stock handler is, you're covered. This also means you can add
|
||||
completely new functionality to KMK by writing your own handler.
|
||||
|
||||
Here's an example of an after_press_handler to change the RGB lights with a layer change:
|
||||
|
||||
```python
|
||||
LOWER = KC.DF(LYR_LOWER) #Set layer to LOWER
|
||||
|
||||
def low_lights(key, keyboard, *args):
|
||||
print('Lower Layer') #serial feedback
|
||||
keyboard.pixels.set_hsv_fill(0, 100, 255) #RGB extension call to set (H,S,V) values
|
||||
|
||||
LOWER.after_press_handler(low_lights) #call the key with the after_press_handler
|
||||
```
|
||||
|
||||
Here's an example of a lifecycle hook to print a giant Shrek ASCII art. It
|
||||
doesn't care about any of the arguments passed into it, because it has no
|
||||
intentions of modifying the internal state. It is purely a [side
|
||||
effect](<https://en.wikipedia.org/wiki/Side_effect_(computer_science)>) run every
|
||||
time Left Alt is pressed:
|
||||
|
||||
```python
|
||||
def shrek(*args, **kwargs):
|
||||
print('⢀⡴⠑⡄⠀⠀⠀⠀⠀⠀⠀⣀⣀⣤⣤⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠸⡇⠀⠿⡀⠀⠀⠀⣀⡴⢿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠑⢄⣠⠾⠁⣀⣄⡈⠙⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⢀⡀⠁⠀⠀⠈⠙⠛⠂⠈⣿⣿⣿⣿⣿⠿⡿⢿⣆⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⢀⡾⣁⣀⠀⠴⠂⠙⣗⡀⠀⢻⣿⣿⠭⢤⣴⣦⣤⣹⠀⠀⠀⢀⢴⣶⣆')
|
||||
print('⠀⠀⢀⣾⣿⣿⣿⣷⣮⣽⣾⣿⣥⣴⣿⣿⡿⢂⠔⢚⡿⢿⣿⣦⣴⣾⠁⠸⣼⡿')
|
||||
print('⠀⢀⡞⠁⠙⠻⠿⠟⠉⠀⠛⢹⣿⣿⣿⣿⣿⣌⢤⣼⣿⣾⣿⡟⠉⠀⠀⠀⠀⠀')
|
||||
print('⠀⣾⣷⣶⠇⠀⠀⣤⣄⣀⡀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠉⠈⠉⠀⠀⢦⡈⢻⣿⣿⣿⣶⣶⣶⣶⣤⣽⡹⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⠀⠉⠲⣽⡻⢿⣿⣿⣿⣿⣿⣿⣷⣜⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣷⣶⣮⣭⣽⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀')
|
||||
print('⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠻⠿⠿⠿⠿⠛⠉')
|
||||
|
||||
return False #Returning True will follow thru the normal handlers sending the ALT key to the OS
|
||||
KC.LALT.before_press_handler(shrek)
|
||||
```
|
||||
|
||||
You can also copy a key without any pre/post handlers attached with `.clone()`,
|
||||
so for example, if I've already added Shrek to my `LALT` but want a Shrek-less
|
||||
`LALT` key elsewhere in my keymap, I can just clone it, and the new key won't
|
||||
have my handlers attached:
|
||||
|
||||
```python
|
||||
SHREKLESS_ALT = KC.LALT.clone()
|
||||
```
|
||||
|
||||
You can also refer to a key by index:
|
||||
|
||||
- `KC['A']`
|
||||
- `KC['NO']`
|
||||
- `KC['LALT']`
|
||||
|
||||
Or the `KC.get` function which has an optional default argument, which will
|
||||
be returned if the key is not found (`default=None` unless otherwise specified):
|
||||
|
||||
- `KC.get('A')`
|
||||
- `KC.get('NO', None)`
|
||||
- `KC.get('NOT DEFINED', KC.RALT)`
|
||||
|
||||
Key names are case-sensitive. `KC['NO']` is different from `KC['no']`. It is recommended
|
||||
that names are normally UPPER_CASE. The exception to this are alpha keys; `KC['A']` and
|
||||
`KC['a']` will both return the same, unshifted, key.
|
26
docs/en/kmkpython_vs_circuitpython.md
Normal file
26
docs/en/kmkpython_vs_circuitpython.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# THIS IS OUT OF DATE. DO NOT USE. ONLY FOR REFERENCE
|
||||
|
||||
## Firmware of choice
|
||||
### KMKPython
|
||||
KMKPython is a fork of CircuitPython, but with libraries for most extensions
|
||||
built in. This saves you from having to get them all and keep them updated
|
||||
yourself. There may be other features added in the future that are exclusive to
|
||||
KMKPython. For the nice!nano, this is highly recommended, and used in place of
|
||||
CircuitPython.
|
||||
Notable differences include
|
||||
- Built in libraries for Bluetooth, RGB, and more
|
||||
- Saves space as builds are optimized for keyboards
|
||||
- Microcontrollers like the nice!nano will be able to access all features out of
|
||||
the box.
|
||||
|
||||
### CircuitPython
|
||||
CircuitPython can be installed by following this guide using the guide
|
||||
[here](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython).
|
||||
It's recommended to run the latest stable version that is at least 5.0 or higher.
|
||||
Beta versions may work, but expect limited support.
|
||||
#### Notable differences include
|
||||
- Supports more devices
|
||||
- Less built in libraries. If using RGB, Bluetooth, and more, you will have to
|
||||
add these libraries yourself
|
||||
- Some devices such as the nice!nano don't have much free space, so not all
|
||||
features can be installed at the same time
|
93
docs/en/layers.md
Normal file
93
docs/en/layers.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Layers
|
||||
Layers module adds keys for accessing other layers. It can simply be added to
|
||||
the extensions list.
|
||||
|
||||
```python
|
||||
from kmk.modules.layers import Layers
|
||||
keyboard.modules.append(Layers())
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|Key |Description |
|
||||
|-----------------|--------------------------------------------------------------------------|
|
||||
|`KC.DF(layer)` |Switches the default layer until the next time the keyboard powers off |
|
||||
|`KC.MO(layer)` |Momentarily activates layer, switches off when you let go |
|
||||
|`KC.LM(layer, mod)` |As `MO(layer)` but with `mod` active |
|
||||
|`KC.LT(layer, kc)` |Momentarily activates layer if held, sends kc if tapped |
|
||||
|`KC.TG(layer)` |Toggles the layer (enables it if no active, and vise versa) |
|
||||
|`KC.TO(layer)` |Activates layer and deactivates all other layers |
|
||||
|`KC.TT(layer)` |Momentarily activates layer if held, toggles it if tapped repeatedly |
|
||||
|
||||
## Custom HoldTap Behavior
|
||||
`KC.TT` and `KC.LT` use the same heuristic to determine taps and holds as
|
||||
ModTap. Check out the [ModTap doc](modtap.md) to find out more.
|
||||
|
||||
## Working with Layers
|
||||
When starting out, care should be taken when working with layers, since it's possible to lock
|
||||
yourself to a layer with no way of returning to the base layer short of unplugging your
|
||||
keyboard. This is especially easy to do when using the `KC.TO()` keycode, which deactivates
|
||||
all other layers in the stack.
|
||||
|
||||
Some helpful guidelines to keep in mind as you design your layers:
|
||||
- Only reference higher-numbered layers from a given layer
|
||||
- Leave keys as `KC.TRNS` in higher layers when they would overlap with a layer-switch
|
||||
|
||||
### Using Multiple Base Layers
|
||||
In some cases, you may want to have more than one base layer (for instance you want to use
|
||||
both QWERTY and Dvorak layouts, or you have a custom gamepad that can switch between
|
||||
different games). In this case, best practice is to have these layers be the lowest, i.e.
|
||||
defined first in your keymap. These layers are mutually-exclusive, so treat changing default
|
||||
layers with `KC.DF()` the same way that you would treat using `KC.TO()`
|
||||
|
||||
## Example Code
|
||||
For our example, let's take a simple 3x3 macropad with two layers as follows:
|
||||
|
||||
```python
|
||||
from kmk.modules.layers import Layers
|
||||
keyboard.modules.append(Layers())
|
||||
|
||||
# Layer Keys
|
||||
MOMENTARY = KC.MO(1)
|
||||
MOD_LAYER = KC.LM(1, KC.RCTL)
|
||||
LAYER_TAP = KC.LT(1, KC.END, prefer_hold=True, tap_interrupted=False, tap_time=250) # any tap longer than 250ms will be interpreted as a hold
|
||||
|
||||
keyboard.keymap = [
|
||||
# Base layer
|
||||
[
|
||||
KC.NO, KC.UP, KC.NO,
|
||||
KC.LEFT,KC.DOWN,KC.RGHT,
|
||||
MOMENTARY, LAYER_TAP, MOD_LAYER,
|
||||
],
|
||||
|
||||
# Function Layer
|
||||
[
|
||||
KC.F1, KC.F2, KC.F3,
|
||||
KC.F4, KC.F5, KC.F6,
|
||||
KC.TRNS,KC.TRNS,KC.TRNS,
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## Advanced Example
|
||||
A common question is: "How do I change RGB background based on my active layer?"
|
||||
Here is _one_ (simple) way of many to go about it.
|
||||
|
||||
```python
|
||||
from kmk.modules.layers import Layers as _Layers
|
||||
from kmk.extensions.rgb import RGB
|
||||
|
||||
rgb = RGB(...) # your RGB configuration goes here
|
||||
keyboard.extensions.append(rgb)
|
||||
|
||||
class Layers(_Layers):
|
||||
last_top_layer = 0
|
||||
hues = (4, 20, 69)
|
||||
|
||||
def after_hid_send(keyboard):
|
||||
if keyboard.active_layers[0] != self.last_top_layer:
|
||||
self.last_top_layer = keyboard.active_layers[0]
|
||||
rgb.set_hsv_fill(self.hues[self.last_top_layer], 255, 255)
|
||||
|
||||
keyboard.modules.append(Layers())
|
||||
```
|
57
docs/en/led.md
Normal file
57
docs/en/led.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# LED (Mono color backlight)
|
||||
Want your keyboard to shine? Add some lights!
|
||||
|
||||
## Enabling the extension
|
||||
The only required values that you need to give the LED extension would be the
|
||||
`led_pin`. It can either be a single board pin, or a list of pins for multiple
|
||||
LED's.
|
||||
```python
|
||||
from kmk.extensions.LED import LED
|
||||
import board
|
||||
|
||||
led_ext = LED(led_pin=[board.GP0, board.GP1])
|
||||
keyboard.extensions.append(led_ext)
|
||||
```
|
||||
|
||||
## [Keycodes]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------------|-------------------|----------------------------|
|
||||
|`KC.LED_TOG()` | |Toggles LED's |
|
||||
|`KC.LED_INC()` | |Increase Brightness |
|
||||
|`KC.LED_DEC()` | |Decrease Brightness |
|
||||
|`KC.LED_SET()` | |Set Brightness |
|
||||
|`KC.LED_ANI` | |Increase animation speed |
|
||||
|`KC.LED_AND` | |Decrease animation speed |
|
||||
|`KC.LED_MODE_PLAIN` |`LED_M_P` |Static LED's |
|
||||
|`KC.LED_MODE_BREATHE` |`LED_M_B` |Breathing animation |
|
||||
|
||||
Keycodes with arguments can affect all, or a selection of LED's.
|
||||
```python
|
||||
# toggle all LEDs
|
||||
LED_TOG_ALL = KC.LED_TOG()
|
||||
|
||||
# increase brightness of first LED
|
||||
LED_INC_0 = KC.LED_INC(0)
|
||||
|
||||
# decrease brightness of second and third LED
|
||||
LED_DEC_1_2 = KC.LED_DEC(1,2)
|
||||
|
||||
```
|
||||
|
||||
## Configuration
|
||||
All of these values can be set by default for when the keyboard boots.
|
||||
```python
|
||||
from kmk.extensions.led import AnimationModes
|
||||
led_ext = LED(
|
||||
led_pin=led_pin,
|
||||
brightness=50,
|
||||
brightness_step=5,
|
||||
brightness_limit=100,
|
||||
breathe_center=1.5,
|
||||
animation_mode=AnimationModes.STATIC,
|
||||
animation_speed=1,
|
||||
user_animation=None,
|
||||
val=100,
|
||||
)
|
||||
```
|
63
docs/en/lock_status.md
Normal file
63
docs/en/lock_status.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Lock Status
|
||||
This extension exposes host-side locks like caps or num lock.
|
||||
|
||||
## Enabling the extension
|
||||
```python
|
||||
from kmk.extensions.lock_status import LockStatus
|
||||
|
||||
locks = LockStatus()
|
||||
keyboard.extensions.append(locks)
|
||||
|
||||
```
|
||||
|
||||
## Read Lock Status
|
||||
Lock states can be retrieved with getter methods and are truth valued -- `True`
|
||||
when the lock is enabled and `False` otherwise.
|
||||
|
||||
|Method |Description |
|
||||
|--------------------------|------------|
|
||||
|`locks.get_num_lock() ` |Num Lock |
|
||||
|`locks.get_caps_lock() ` |Caps Lock |
|
||||
|`locks.get_scroll_lock() `|Scroll Lock |
|
||||
|`locks.get_compose() ` |Compose |
|
||||
|`locks.get_kana() ` |Kana |
|
||||
|
||||
|
||||
## React to Lock Status Changes
|
||||
The best way to react to changes in lock status is to extend
|
||||
the LockStatus class. When a lock status change happens,
|
||||
the 'after_hid_send' function is envoked so you would override
|
||||
LockStatus's to inject your own logic. Be aware though that
|
||||
this function is also critically important to the functionality
|
||||
of LockStatus so be sure to invoke the 'super()' version of your
|
||||
class to trigger the default functionality of LockStatus.
|
||||
|
||||
```python
|
||||
# in your main.py
|
||||
from kb import KMKKeyboard
|
||||
from kmk.extensions.lock_status import LockStatus
|
||||
from kmk.extensions.LED import LED
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
leds = LED(led_pin=[board.GP27, board.GP28])
|
||||
|
||||
class LEDLockStatus(LockStatus):
|
||||
def set_lock_leds(self):
|
||||
if self.get_caps_lock():
|
||||
leds.set_brightness(50, leds=[0])
|
||||
else:
|
||||
leds.set_brightness(0, leds=[0])
|
||||
|
||||
if self.get_scroll_lock():
|
||||
leds.set_brightness(50, leds=[1])
|
||||
else:
|
||||
leds.set_brightness(0, leds=[1])
|
||||
|
||||
def after_hid_send(self, sandbox):
|
||||
super().after_hid_send(sandbox) # Critically important. Do not forget
|
||||
if self.report_updated:
|
||||
self.set_lock_leds()
|
||||
|
||||
keyboard.extensions.append(leds)
|
||||
keyboard.extensions.append(LEDLockStatus())
|
||||
```
|
25
docs/en/media_keys.md
Normal file
25
docs/en/media_keys.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Media Keys
|
||||
Media keys extension adds keys for common media control keys. It can simply be
|
||||
added to the extensions list.
|
||||
|
||||
```python
|
||||
from kmk.extensions.media_keys import MediaKeys
|
||||
keyboard.extensions.append(MediaKeys())
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------|--------------------|-----------------------------------------------|
|
||||
|`KC.AUDIO_MUTE` |`KC.MUTE` |Mute |
|
||||
|`KC.AUDIO_VOL_UP` |`KC.VOLU` |Volume Up |
|
||||
|`KC.AUDIO_VOL_DOWN` |`KC.VOLD` |Volume Down |
|
||||
|`KC.BRIGHTNESS_UP` |`KC.BRIU` |Brightness Up |
|
||||
|`KC.BRIGHTNESS_DOWN` |`KC.BRID` |Brightness Down |
|
||||
|`KC.MEDIA_NEXT_TRACK` |`KC.MNXT` |Next Track (Windows) |
|
||||
|`KC.MEDIA_PREV_TRACK` |`KC.MPRV` |Previous Track (Windows) |
|
||||
|`KC.MEDIA_STOP` |`KC.MSTP` |Stop Track (Windows) |
|
||||
|`KC.MEDIA_PLAY_PAUSE` |`KC.MPLY` |Play/Pause Track |
|
||||
|`KC.MEDIA_EJECT` |`KC.EJCT` |Eject (macOS) |
|
||||
|`KC.MEDIA_FAST_FORWARD`|`KC.MFFD` |Next Track (macOS) |
|
||||
|`KC.MEDIA_REWIND` |`KC.MRWD` |Previous Track (macOS) |
|
18
docs/en/midi.md
Normal file
18
docs/en/midi.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# MIDI
|
||||
The MIDI module adds keymap entries for sending MIDI data streams. It will require adding the `adafruit_midi` library from the [Adafruit CircuitPython Bundle](https://circuitpython.org/libraries) to your device's folder.
|
||||
Add it to your keyboard's modules list with:
|
||||
|
||||
```python
|
||||
from kmk.modules.midi import MidiKeys
|
||||
keyboard.modules.append(MidiKeys())
|
||||
```
|
||||
## Keycodes
|
||||
|
||||
|Key |Description |
|
||||
|-------------------------------|----------------------------------------------------------|
|
||||
|`KC.MIDI_CC(ctrl, val)` |Sends a ControlChange message |
|
||||
|`KC.MIDI_NOTE(note, velo)` |Sends a Note message |
|
||||
|`KC.MIDI_PB(val)` |Sends a Pitch Wheel message |
|
||||
|`KC.MIDI_PC(program)` |Sends a Program Change message |
|
||||
|`KC.MIDI_START()` |Sends a Start message |
|
||||
|`KC.MIDI_STOP()` |Sends a Stop message |
|
51
docs/en/modtap.md
Normal file
51
docs/en/modtap.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# ModTap Keycodes
|
||||
Enabling ModTap will give you access to the following keycodes and can simply be
|
||||
added to the modules list.
|
||||
|
||||
```python
|
||||
from kmk.modules.modtap import ModTap
|
||||
modtap = ModTap()
|
||||
# optional: set a custom tap timeout in ms
|
||||
# modtap.tap_time = 300
|
||||
keyboard.modules.append(modtap)
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|New Keycode | Description |
|
||||
|---------------------------------------------------------|-----------------------------------------------------------------|
|
||||
|`LCTL = KC.MT(KC.SOMETHING, KC.LCTRL)` |`LCTRL` if held `kc` if tapped |
|
||||
|`LSFT = KC.MT(KC.SOMETHING, KC.LSFT)` |`LSHIFT` if held `kc` if tapped |
|
||||
|`LALT = KC.MT(KC.SOMETHING, KC.LALT)` |`LALT` if held `kc` if tapped |
|
||||
|`LGUI = KC.MT(KC.SOMETHING, KC.LGUI)` |`LGUI` if held `kc` if tapped |
|
||||
|`RCTL = KC.MT(KC.SOMETHING, KC.RCTRL)` |`RCTRL` if held `kc` if tapped |
|
||||
|`RSFT = KC.MT(KC.SOMETHING, KC.RSFT)` |`RSHIFT` if held `kc` if tapped |
|
||||
|`RALT = KC.MT(KC.SOMETHING, KC.RALT)` |`RALT` if held `kc` if tapped |
|
||||
|`RGUI = KC.MT(KC.SOMETHING, KC.RGUI)` |`RGUI` if held `kc` if tapped |
|
||||
|`SGUI = KC.MT(KC.SOMETHING, KC.LSHFT(KC.LGUI))` |`LSHIFT` and `LGUI` if held `kc` if tapped |
|
||||
|`LCA = KC.MT(KC.SOMETHING, KC.LCTRL(KC.LALT))` |`LCTRL` and `LALT` if held `kc` if tapped |
|
||||
|`LCAG = KC.MT(KC.SOMETHING, KC.LCTRL(KC.LALT(KC.LGUI)))` |`LCTRL` and `LALT` and `LGUI` if held `kc` if tapped |
|
||||
|`MEH = KC.MT(KC.SOMETHING, KC.LCTRL(KC.LSFT(KC.LALT)))` |`CTRL` and `LSHIFT` and `LALT` if held `kc` if tapped |
|
||||
|`HYPR = KC.MT(KC.SOMETHING, KC.HYPR)` |`LCTRL` and `LSHIFT` and `LALT` and `LGUI` if held `kc` if tapped|
|
||||
|
||||
## Custom HoldTap Behavior
|
||||
The full ModTap signature is as follows:
|
||||
```python
|
||||
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=HoldTapRepeat.NONE)
|
||||
```
|
||||
* `prefer_hold`: decides which keycode the ModTap key resolves to when another
|
||||
key is pressed before the timeout finishes. When `True` the hold keycode is
|
||||
chosen, the tap keycode when `False`.
|
||||
* `tap_interrupted`: decides if the timeout will interrupt at the first other
|
||||
key press/down, or after the first other key up/release. Set to `True` for
|
||||
interrupt on release.
|
||||
* `tap_time`: length of the tap timeout in milliseconds.
|
||||
* `repeat`: decides how to interpret repeated presses if they happen within
|
||||
`tap_time` after a release.
|
||||
* `TAP`: repeat tap action, if previous action was a tap.
|
||||
* `HOLD`: repeat hold action, if previous action was a hold.
|
||||
* `ALL`: repeat all of the above.
|
||||
* `NONE`: no repeat action (default), everything works as expected.
|
||||
The `HoldTapRepeat` enum must be imported from `kmk.modules.holdtap`.
|
||||
|
||||
Each of these parameters can be set for every ModTap key individually.
|
34
docs/en/modules.md
Normal file
34
docs/en/modules.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Modules
|
||||
Modules, unlike extensions, change how your keyboard works. These are meant to have
|
||||
the ability to alter the core code in any way. Unlike extensions, these are not in a
|
||||
sandbox, and can make massive changes to normal operation.
|
||||
|
||||
## Core Modules
|
||||
These modules are provided in all builds and can be enabled. Currently offered
|
||||
modules are
|
||||
|
||||
- [Combos](combos.md): Adds chords and sequences
|
||||
- [Layers](layers.md): Adds layer support (Fn key) to allow many more keys to be
|
||||
put on your keyboard.
|
||||
- [ModTap](modtap.md): Adds support for augmented modifier keys to act as one key
|
||||
when tapped, and modifier when held.
|
||||
- [Mouse keys](mouse_keys.md): Adds mouse keycodes.
|
||||
- [OneShot](oneshot.md): Adds support for oneshot/sticky keys.
|
||||
- [Power](power.md): Power saving features. This is mostly useful when on battery power.
|
||||
- [Split](split_keyboards.md): Keyboards split in two. Seems ergonomic!
|
||||
- [SerialACE](serialace.md): [DANGER - _see module README_] Arbitrary Code Execution over the data serial.
|
||||
- [TapDance](tapdance.md): Different key actions depending on how often it is pressed.
|
||||
- [Dynamic Sequences](dynamic_sequences.md): Records a sequence of keypresses and plays it back.
|
||||
|
||||
### Require Libraries
|
||||
These modules can be used without specific hardware, but require additional libraries such as the `Adafruit CircuitPython Bundle`.
|
||||
|
||||
- [MIDI](midi.md): Adds sending MIDI data in the form of keymap entries.
|
||||
|
||||
|
||||
### Peripherals
|
||||
These modules are for specific hardware and may require additional libraries to function.
|
||||
- [ADNS9800](adns9800.md): Controlling ADNS9800 optical sensor.
|
||||
- [Encoder](encoder.md): Handling rotary encoders.
|
||||
- [Pimoroni trackball](pimoroni_trackball.md): Handling a small I2C trackball made by Pimoroni.
|
||||
- [AS5013 aka EasyPoint](easypoint.md): Handling a small I2C magnetic position sensor made by AMS.
|
23
docs/en/mouse_keys.md
Normal file
23
docs/en/mouse_keys.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Mouse keys
|
||||
|
||||
To enable mouse cursor and/or mouse buttons control from the keyboard add this
|
||||
module to list:
|
||||
|
||||
```python
|
||||
from kmk.modules.mouse_keys import MouseKeys
|
||||
keyboard.modules.append(MouseKeys())
|
||||
```
|
||||
|
||||
# Keycodes
|
||||
|
||||
| Keycode | Description |
|
||||
|---------------------------|--------------------------------------|
|
||||
| `KC.MB_LMB` | Left mouse button |
|
||||
| `KC.MB_RMB` | Right mouse button |
|
||||
| `KC.MB_MMB` | Middle mouse button |
|
||||
| `KC.MW_UP` | Mouse wheel up |
|
||||
| `KC.MW_DOWN`, `KC.MW_DN` | Mouse wheel down |
|
||||
| `KC.MS_UP` | Move mouse cursor up |
|
||||
| `KC.MS_DOWN`, `KC.MS_DN` | Move mouse cursor down |
|
||||
| `KC.MS_LEFT`, `KC.MS_LT` | Move mouse cursor left |
|
||||
| `KC.MS_RIGHT`, `KC.MS_RT` | Move mouse cursor right |
|
104
docs/en/oneshot.md
Normal file
104
docs/en/oneshot.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# OneShot Keycodes
|
||||
|
||||
OneShot keys or sticky keys enable you to have keys that keep staying pressed
|
||||
for a certain time or until another key is pressed and released.
|
||||
If the timeout expires or other keys are pressed, and the sticky key wasn't
|
||||
released, it is handled as a regular key hold.
|
||||
|
||||
## Enable OneShot Keys
|
||||
|
||||
```python
|
||||
from kmk.modules.oneshot import OneShot
|
||||
oneshot = OneShot()
|
||||
# optional: set a custom tap timeout in ms (default: 1000ms)
|
||||
# oneshot.tap_time = 1500
|
||||
keyboard.modules.append(oneshot)
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|Keycode | Aliases |Description |
|
||||
|-----------------|--------------|----------------------------------|
|
||||
|`KC.OS(KC.ANY)` | `KC.ONESHOT` |make a sticky version of `KC.ANY` |
|
||||
|
||||
`KC.ONESHOT` accepts any valid key code as argument, including modifiers and KMK
|
||||
internal keys like momentary layer shifts.
|
||||
|
||||
## Custom OneShot Behavior
|
||||
|
||||
The full OneShot signature is as follows:
|
||||
|
||||
```python
|
||||
KC.OS(
|
||||
KC.TAP, # the sticky keycode
|
||||
tap_time=None # length of the tap timeout in milliseconds
|
||||
)
|
||||
```
|
||||
|
||||
## OneShot Modifier Combinations
|
||||
|
||||
The OneShot module by default cannot apply two OneShot modifiers to another key. To get around this you can use the [Combos](combos.md) module. Below is a minimal example that allows for multiple OneShot modifiers to apply to the next key pressed. In this example you can tap either of the OneShot keys then tap the other and finally tap `p` and that will send `ctrl+shift+p`.
|
||||
|
||||
```python
|
||||
from kmk.modules.combos import Chord, Combos
|
||||
from kmk.modules.oneshot import OneShot
|
||||
|
||||
oneshot = OneShot()
|
||||
keyboard.modules.append(oneshot)
|
||||
|
||||
OS_LCTL = KC.OS(KC.LCTL, tap_time=None)
|
||||
OS_LSFT = KC.OS(KC.LSFT, tap_time=None)
|
||||
|
||||
combos = Combos()
|
||||
keyboard.modules.append(combos)
|
||||
|
||||
combos.combos = [
|
||||
Chord((OS_LCTL, OS_LSFT), OS_LCTL_LSFT, timeout=1000),
|
||||
]
|
||||
|
||||
keyboard.keymap = [[OS_LSFT, OS_LCTL, KC.P]]
|
||||
```
|
||||
|
||||
Below is the complete list of OneShot and Chords you need to allow any combination of modifiers (left modifiers only).
|
||||
|
||||
> <details>
|
||||
> <summary>Long code chunk (click to load)</summary>
|
||||
>
|
||||
> ```python
|
||||
> OS_LCTL = KC.OS(KC.LCTL, tap_time=None)
|
||||
> OS_LSFT = KC.OS(KC.LSFT, tap_time=None)
|
||||
> OS_LGUI = KC.OS(KC.LGUI, tap_time=None)
|
||||
> OS_LALT = KC.OS(KC.LALT, tap_time=None)
|
||||
>
|
||||
> OS_LCTL_LSFT = KC.OS(KC.LCTL(OS_LSFT), tap_time=None)
|
||||
> OS_LCTL_LALT = KC.OS(KC.LCTL(OS_LALT), tap_time=None)
|
||||
> OS_LCTL_LGUI = KC.OS(KC.LCTL(OS_LGUI), tap_time=None)
|
||||
> OS_LSFT_LALT = KC.OS(KC.LSFT(OS_LALT), tap_time=None)
|
||||
> OS_LSFT_LGUI = KC.OS(KC.LSFT(OS_LGUI), tap_time=None)
|
||||
> OS_LALT_LGUI = KC.OS(KC.LALT(OS_LGUI), tap_time=None)
|
||||
>
|
||||
> OS_LCTL_LSFT_LGUI = KC.OS(KC.LCTL(KC.LSFT(OS_LGUI)), tap_time=None)
|
||||
> OS_LCTL_LSFT_LALT = KC.OS(KC.LCTL(KC.LSFT(OS_LALT)), tap_time=None)
|
||||
> OS_LCTL_LALT_LGUI = KC.OS(KC.LCTL(KC.LALT(OS_LGUI)), tap_time=None)
|
||||
> OS_LSFT_LALT_LGUI = KC.OS(KC.LSFT(KC.LALT(OS_LGUI)), tap_time=None)
|
||||
>
|
||||
> OS_LCTL_LSFT_LALT_LGUI = KC.OS(KC.LCTL(KC.LSFT(KC.LALT(OS_LGUI))), tap_time=None)
|
||||
>
|
||||
> combos.combos = [
|
||||
> Chord((OS_LCTL, OS_LSFT), OS_LCTL_LSFT, timeout=1000),
|
||||
> Chord((OS_LCTL, OS_LALT), OS_LCTL_LALT, timeout=1000),
|
||||
> Chord((OS_LCTL, OS_LGUI), OS_LCTL_LGUI, timeout=1000),
|
||||
> Chord((OS_LSFT, OS_LALT), OS_LSFT_LALT, timeout=1000),
|
||||
> Chord((OS_LSFT, OS_LGUI), OS_LSFT_LGUI, timeout=1000),
|
||||
> Chord((OS_LALT, OS_LGUI), OS_LALT_LGUI, timeout=1000),
|
||||
>
|
||||
> Chord((OS_LCTL, OS_LSFT, OS_LGUI), OS_LCTL_LSFT_LGUI, timeout=1000),
|
||||
> Chord((OS_LCTL, OS_LSFT, OS_LALT), OS_LCTL_LSFT_LALT, timeout=1000),
|
||||
> Chord((OS_LCTL, OS_LALT, OS_LGUI), OS_LCTL_LALT_LGUI, timeout=1000),
|
||||
> Chord((OS_LSFT, OS_LALT, OS_LGUI), OS_LSFT_LALT_LGUI, timeout=1000),
|
||||
>
|
||||
> Chord((OS_LCTL, OS_LSFT, OS_LALT, OS_LGUI), OS_LCTL_LSFT_LALT_LGUI, timeout=1000),
|
||||
> ]
|
||||
> ```
|
||||
>
|
||||
> </details>
|
109
docs/en/peg_oled_display.md
Normal file
109
docs/en/peg_oled_display.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Peg Oled Display
|
||||
To use this you need to make some changes to your kb.py as well as you main.py I will break it into two sections for you.
|
||||
|
||||
### What you can and cant do
|
||||
|
||||
#### Can do
|
||||
* Display images
|
||||
* Display text
|
||||
* Set images or text to react to your layer
|
||||
|
||||
|
||||
#### Cant do yet / on the way
|
||||
* React to battery percentage
|
||||
* React to WPM
|
||||
|
||||
## Required Libs
|
||||
You need these frozen into your circuitpython or in a lib folder at the root of your drive.
|
||||
* [Adafruit_CircuitPython_DisplayIO_SSD1306](https://github.com/adafruit/Adafruit_CircuitPython_DisplayIO_SSD1306)
|
||||
* [Adafruit_CircuitPython_Display_Text](https://github.com/adafruit/Adafruit_CircuitPython_Display_Text)
|
||||
* [Download .mpy versions from here](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20220415/adafruit-circuitpython-bundle-7.x-mpy-20220415.zip)
|
||||
|
||||
|
||||
## kb.py
|
||||
Your chosen board may already have these changes done If not you will need to add them.
|
||||
|
||||
You need to add SCL and SDA to your keyboard. The pins the oled are connected to on your controller may not be called "SCL"and "SDA" they could be "GP21" and "GP13" for example. The best way to find out what they are is look at the boards pinout.
|
||||
```python
|
||||
SCL=board.SCL
|
||||
SDA=board.SDA
|
||||
```
|
||||
|
||||
|
||||
## Main.py
|
||||
These are the changes that need to be made / added to your main.py
|
||||
### Config
|
||||
No mater how you are going to use the oled you need this part
|
||||
```python
|
||||
from kmk.extensions.peg_oled_Display import Oled,OledDisplayMode,OledReactionType,OledData
|
||||
keyboard = KMKKeyboard()
|
||||
# ... Other oled code
|
||||
keyboard.extensions.append(oled_ext)
|
||||
|
||||
```
|
||||
### Photos
|
||||
So the config for photos is quite simple. Getting the photos maybe not so much. I will explain.
|
||||
|
||||
Oled takes 2-3 arguments
|
||||
|
||||
1. OledData
|
||||
* OledData can take image **or** corner_one, corner_two, corner_three and corner_four
|
||||
* Every item in OledData has 2 fields
|
||||
* 0: This is the reaction type right now it can be OledReactionType.LAYER or OledReactionType.STATIC
|
||||
* 1: An array of the items you want to show for the reaction. In this example 4 images to switch on the 4 layers
|
||||
2. To display called as "toDisplay=OledDisplayMode.TXT" this takes a OledDisplayMode TXT or IMG.
|
||||
* This tells the extension to load images or text.
|
||||
3. Flip called as "flip= Boolean" this will simply flip your display.
|
||||
|
||||
|
||||
```python
|
||||
oled_ext = Oled(OledData(image={0:OledReactionType.LAYER,1:["1.bmp","2.bmp","1.bmp","2.bmp"]}),toDisplay=OledDisplayMode.IMG,flip=False)
|
||||
```
|
||||
In this code example we are saying to react to the layer change and load a image filed named "1.bmp" for layer one and "2.bmp" for layer two and so on.
|
||||
|
||||
### Preparing the images
|
||||
So you need to include all the images in your circuitpython drive in the root along side main.py
|
||||
|
||||
Your images need to be 128x32px and should only be black and white. After you have made your image you can save as a "monochrome bmp" this will save you a lot of space.
|
||||
|
||||
### Text
|
||||
Ok now we get into something that looks a lot more complicated but we will get though it.
|
||||
|
||||
Almost everything is the same We swap toDisplay to TXT and there are more items in the OledData Class, lets get into that.
|
||||
|
||||
1. Top left
|
||||
2. Top right
|
||||
3. Bottom left
|
||||
4. Bottom right
|
||||
|
||||
After that is the same as the previous example. Each one has two fields 0:the reaction type. 1:what to display
|
||||
|
||||
In this code we will always display the word "layer" in the top left corner of the screen then the other 3 corners will swap depending on the layer.
|
||||
|
||||
|
||||
|
||||
```python
|
||||
oled_ext = Oled(
|
||||
OledData(
|
||||
corner_one={0:OledReactionType.STATIC,1:["layer"]},
|
||||
corner_two={0:OledReactionType.LAYER,1:["1","2","3","4"]},
|
||||
corner_three={0:OledReactionType.LAYER,1:["base","raise","lower","adjust"]},
|
||||
corner_four={0:OledReactionType.LAYER,1:["qwerty","nums","shifted","leds"]}
|
||||
),
|
||||
toDisplay=OledDisplayMode.TXT,flip=False)
|
||||
```
|
||||
|
||||
### Note
|
||||
Your oled data can be a variable as shown below with images.
|
||||
|
||||
```python
|
||||
oled_display_data=OledData(image={0:OledReactionType.LAYER,1:["1.bmp","2.bmp","1.bmp","2.bmp"]})
|
||||
|
||||
oled_ext = Oled(oled_display_data,toDisplay=OledDisplayMode.IMG,flip=False)
|
||||
```
|
||||
|
||||
### Text example
|
||||
|
||||
|
||||

|
||||
|
203
docs/en/peg_rgb_matrix.md
Normal file
203
docs/en/peg_rgb_matrix.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# Peg RGB Matrix
|
||||
|
||||
## What you can and cannot do with this extension:
|
||||
|
||||
### Can Do
|
||||
|
||||
* Set any key's LED to be any color in a syntax very similar to your keymap
|
||||
* Allows specific keys to be set to OFF
|
||||
* Allows underglow LEDs to be a different color than per-key LEDs
|
||||
* Allows modifier keys to be set to a different color than alpha keys
|
||||
* Full split keyboard support
|
||||
* Change brightness of LEDs from code or using keycodes
|
||||
|
||||
### Cannot Do (currently in progress)
|
||||
|
||||
* Adjust color at runtime. Currently the extension requires changes to main.py in order to make changes to your LEDs.
|
||||
* Animations
|
||||
* Change LED color based on current layer
|
||||
|
||||
## Keycodes
|
||||
|
||||
Currently this extension does not support changing LEDs at runtime, as a result there are only three keycodes available to interact with this extension,those are:
|
||||
|
||||
* `KC.RGB_TOG`. This keycode simply toggles all your LEDs on and off.
|
||||
* `KC.RGB_BRI`. This keycode increases the brightness of the LEDs.
|
||||
* `KC.RGB_BRD`. This keycode decreases the brightness of the LEDs.
|
||||
|
||||
## Required Libraries
|
||||
|
||||
The following libraries must be frozen in your CircuitPython distribution or in a 'lib' folder at the root of your drive.
|
||||
|
||||
* [Adafruit_CircuitPython_NeoPixel](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel)
|
||||
* [Download .mpy versions from here](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/download/20220415/adafruit-circuitpython-bundle-7.x-mpy-20220415.zip)
|
||||
|
||||
## Required Changes to main.py and kb.py
|
||||
|
||||
In order to use this extension the user must make changes to both their kb.py and main.py files. Below you will find a more comprehensive list of changes required in order to use this extension.
|
||||
|
||||
### kb.py
|
||||
|
||||
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
||||
|
||||
The board's kb.py needs 3 fields:
|
||||
|
||||
* LED Key Position `led_key_pos`
|
||||
* Much like `coord_mapping` this tells the extension where the LEDs are on your board.
|
||||
* Brightness Limit `brightness_limit`
|
||||
* Limits your brightness and may be required in order to stabilize performance.
|
||||
* Number of LEDs `num_pixels`
|
||||
* Used for calculations in order to ensure the LEDs map to the correct keys.
|
||||
|
||||
#### Non-split Example:
|
||||
|
||||
Below shows a simple non-split example for a board containing 48 LEDs total and 38 keys with per-key LEDs.
|
||||
This means we will have 10 underglow LEDs and 38 per-key LEDs.
|
||||
For our example we will assume (because it is most common) the underglow LEDs are connected before the per-key LEDs.
|
||||
Starting from 0, indexes 0-9 are all underglow, so our `led_key_pos` array starts at 10, the `led_key_pos` array always starts with the key in the upper left position on the board.
|
||||
Our example is wired in such a way where the positions layout naturally and each row simply increases by 1 starting at the upper left of the board.
|
||||
Of course if your board's LEDs are layed out different, your `led_key_pos` will need to match that layout.
|
||||
|
||||
Underglow LEDs always appear at the end of the `led_key_pos` array, because the array always starts with per-key LEDs.
|
||||
|
||||
```python
|
||||
led_key_pos=[
|
||||
10,11,12,13,14,15,16,17,18,19,
|
||||
20,21,22,23,24,25,26,27,28,29,
|
||||
30,31,32,33,34,35,36,37,38,39,
|
||||
40,41,42,43,44,45,46,47,
|
||||
5, 6, 7, 8, 9,
|
||||
0, 1, 2, 3, 4
|
||||
]
|
||||
brightness_limit = 1.0
|
||||
num_pixels = 48
|
||||
```
|
||||
|
||||
#### Split Example:
|
||||
|
||||
Below shows a 58 key split keyboard's `led_key_pos` array for a board containing 70 LEDs in total.
|
||||
The board has 58 keys, meaning we are left with 12 underglow LEDs total.
|
||||
Since the board is a split and we can assume the LEDs are mirrored, that means each half has 29 per-key LEDs and 6 underglow LEDs.
|
||||
|
||||
Let's first focus on the left half of the board.
|
||||
In this example the underglow LEDs are again connected first, and this half has 6 underglow LEDs.
|
||||
Starting from position 0 this means 0-5 are underglow LEDs and our per-key lighting starts at 6.
|
||||
Our example board is wired in such a way where the left half's first per-key LED is position in the upper right corner of that half.
|
||||
The LEDs then incremement towards the right and follow a 'zig-zag' pattern until all are accounted for (6-34).
|
||||
|
||||
Examining the other half (the right side) you'll notice the LEDs are connected in a similar way but mirrored.
|
||||
The right half's LEDs start in the upper left position of the board and increment towards the right, and then follow a 'zig-zag' pattern until all are accounted for (41-69).
|
||||
|
||||
Underglow LEDs always appear at the end of the `led_key_pos` array, because the array always starts with per-key LEDs.
|
||||
|
||||
```python
|
||||
led_key_pos =[
|
||||
11,10,9 ,8 ,7 ,6 , 41,42,43,44,45,46,
|
||||
12,13,14,15,16,17, 52,51,50,49,48,47,
|
||||
23,22,21,20,19,18, 53,54,55,56,57,58,
|
||||
24,25,26,27,28,29,30, 65,64,63,62,61,60,59,
|
||||
34,33,32,31, 66,67,68,69,
|
||||
3 ,4 ,5 , 40,39,38,
|
||||
2 ,1 ,0 , 35,36,37
|
||||
]
|
||||
brightness_limit = 1.0
|
||||
num_pixels = 70
|
||||
|
||||
```
|
||||
|
||||
### main.py
|
||||
|
||||
It is possible your chosen board may already have these changes made, if not you will need to make these additions:
|
||||
|
||||
```python
|
||||
from kmk.extensions.peg_rgb_matrix import Rgb_matrix,Rgb_matrix_data,Color
|
||||
# ... Other code
|
||||
rgb_ext = Rgb_matrix(...per key color data)
|
||||
keyboard.extensions.append(rgb_ext)
|
||||
```
|
||||
|
||||
Rgb_matrix extension requires one argument (`Rgb_matrix_data`), although additional arguments can be passed, here are all arguments that can be passed to
|
||||
|
||||
Rgb_matrix:
|
||||
|
||||
* LED Display `ledDisplay`
|
||||
* This is our primary and only required field, this takes a `Rgb_matrix_data` class.
|
||||
* Rgb_matrix_data only takes two fields:
|
||||
* Keys: an array of colors with a length equal to the number of keys on your keyboard
|
||||
* Underglow: an array of colors with a length equal to the number of underglow leds on your keyboard
|
||||
* Split `split`
|
||||
* This is an optional boolean and only to be used if the keyboard is a split.
|
||||
* Right Side `rightSide`
|
||||
* This is optional boolean only to be used if the keyboard is split. This signals that this configuration is targetting the right side (off side).
|
||||
* RGB Order `rgb_order`
|
||||
* This is optional and only needs to be set if you are not using a WS2812 based LED.
|
||||
* Disable Auto Write `disable_auto_write`
|
||||
* This is optional and only serves to make all your LEDs turn on at once instead of animate to their on state.
|
||||
|
||||
### Colors
|
||||
|
||||
Colors are RGB and can be provided in one of two ways.
|
||||
Colors can be defined as an array of three numbers (0-255) or you can use the `Color` class with its default colors, see example below.
|
||||
|
||||
#### Passing RGB Codes
|
||||
|
||||
```python
|
||||
Rgb_matrix_data(
|
||||
keys=[[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],"""... rest of colors""" ],
|
||||
underglow=[[0,0,55],[0,0,55],"""... rest of colors""" ]
|
||||
)
|
||||
```
|
||||
|
||||
#### Using `Color` Class
|
||||
|
||||
```python
|
||||
Rgb_matrix_data(
|
||||
keys=[Color.RED, Color.GREEN, Color.BLUE, Color.WHITE, Color.YELLOW, Color.ORANGE,"""... rest of colors""" ],
|
||||
underglow=[Color.PURPLE, Color.TEAL, Color.PINK, Color.OFF,"""... rest of colors""" ]
|
||||
)
|
||||
```
|
||||
|
||||
### Full Examples
|
||||
|
||||
```python
|
||||
rgb_ext = Rgb_matrix(ledDisplay=Rgb_matrix_data(
|
||||
keys=[
|
||||
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
|
||||
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
|
||||
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55], [55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
|
||||
[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],[255,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[55,55,55],[255,55,55],
|
||||
[255,55,55],[55,55,55],[55,55,55],[255,55,55],[255,55,55],[55,55,55],[55,55,55],[255,55,55]],
|
||||
|
||||
underglow=[
|
||||
[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55],[0,0,55]]
|
||||
),
|
||||
split=True,
|
||||
rightSide=True,
|
||||
disable_auto_write=True)
|
||||
```
|
||||
|
||||
#### Bonus
|
||||
|
||||
Because creating `ledDisplay` can be time consuming, there is a utility avaiable that will generate a basic framework for you.
|
||||
|
||||
```python
|
||||
Rgb_matrix_data.generate_led_map(58,10,Color.WHITE,Color.BLUE)
|
||||
```
|
||||
|
||||
Call `Rgb_matrix_data.generate_led_map` before you do any configuration beyond imports and it will print an `Rgb_matrix_data` class to your CircuitPython REPL which you can view by using a tool like "screen" or "PUTTY".
|
||||
|
||||
Generate LED Map Arguments:
|
||||
|
||||
* Number of Keys
|
||||
* Number of Underglow
|
||||
* Key Color
|
||||
* Underglow Color
|
||||
|
||||
Example Using Above Arguments:
|
||||
|
||||
```python
|
||||
Rgb_matrix_data(keys=[[249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249], [249, 249, 249]],
|
||||
underglow=[[0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255], [0, 0, 255]])
|
||||
```
|
||||
|
||||
[Connecting to the Serial Console](https://learn.adafruit.com/welcome-to-circuitpython/kattni-connecting-to-the-serial-console)
|
61
docs/en/pimoroni_trackball.md
Normal file
61
docs/en/pimoroni_trackball.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Pimoroni Trackball
|
||||
|
||||
Module handles usage of Trackball Breakout by Pimoroni.
|
||||
|
||||
Product page: https://shop.pimoroni.com/products/trackball-breakout
|
||||
|
||||
### Usage
|
||||
|
||||
Declare I2C bus and add this module in your main class.
|
||||
|
||||
```python
|
||||
from kmk.modules.pimoroni_trackball import Trackball, TrackballMode
|
||||
import busio as io
|
||||
|
||||
i2c = io.I2C(scl=board.D3, sda=board.D2)
|
||||
trackball = Trackball(i2c)
|
||||
keyboard.modules.append(trackball)
|
||||
```
|
||||
|
||||
Module will also work when you cannot use `busio` and do `import bitbangio as io` instead.
|
||||
|
||||
### Key inputs, other handler combinations
|
||||
|
||||
If you have used this thing on a mobile device, you will know it excels at cursor movement
|
||||
|
||||
```python
|
||||
|
||||
from kmk.modules.pimoroni_trackball import Trackball, TrackballMode, PointingHandler, KeyHandler, ScrollHandler, ScrollDirection
|
||||
|
||||
trackball = Trackball(i2c, mode=TrackballMode.MOUSE_MODE, handlers=[
|
||||
# act like an encoder, input arrow keys
|
||||
KeyHandler(KC.UP, KC.RIGHT, KC.DOWN, KC.LEFT, KC.ENTER),
|
||||
# on layer 1 and above use the default pointing behavior
|
||||
PointingHandler(),
|
||||
# use ScrollDirection.NATURAL (default) or REVERSE to change the scrolling direction
|
||||
ScrollHandler(scroll_direction=ScrollDirection.NATURAL)
|
||||
])
|
||||
|
||||
# now you can use these KeyCodes:
|
||||
|
||||
KC.TB_NEXT_HANDLER # rotates through available
|
||||
KC.TB_HANDLER(0) # activate KeyHandler
|
||||
KC.TB_HANDLER(1) # activate MouseHandler
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Backlight
|
||||
|
||||
Setup backlight color using below commands:
|
||||
|
||||
```python
|
||||
trackball.set_rgbw(r, g, b, w)
|
||||
trackball.set_red(brightness)
|
||||
trackball.set_green(brightness)
|
||||
trackball.set_blue(brightness)
|
||||
trackball.set_white(brightness)
|
||||
```
|
||||
|
||||
This module exposes one keycode `TB_MODE`, which on hold switches between `MOUSE_MODE` and `SCROLL_MODE`.
|
||||
To choose the default mode, pass it in `Trackball` constructor.
|
BIN
docs/en/pins56.jpg
Normal file
BIN
docs/en/pins56.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 222 KiB |
129
docs/en/porting_to_kmk.md
Normal file
129
docs/en/porting_to_kmk.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Porting to KMK
|
||||
Porting a board to KMK is quite simple, and follows this base format.
|
||||
|
||||
```python
|
||||
import board
|
||||
|
||||
from kmk.kmk_keyboard import KMKKeyboard as _KMKKeyboard
|
||||
from kmk.scanners import DiodeOrientation
|
||||
{EXTENSIONS_IMPORT}
|
||||
|
||||
class KMKKeyboard(_KMKKeyboard):
|
||||
{REQUIRED}
|
||||
extensions = []
|
||||
|
||||
```
|
||||
|
||||
## REQUIRED
|
||||
This is designed to be replaced with the defining pins of your keyboard. Rows,
|
||||
columns and the diode direction (if any), should be defined like this
|
||||
```python
|
||||
row_pins = [board.p0_31, board.p0_29, board.p0_02, board.p1_15]
|
||||
col_pins = [board.p0_22, board.p0_24, board.p1_00, board.p0_11, board.p1_04]
|
||||
diode_orientation = DiodeOrientation.COL2ROW
|
||||
```
|
||||
|
||||
## Additional pins for extensions
|
||||
KMK includes built in extensions for RGB and split keyboards, and powersave. If
|
||||
these are applicable on your keyboard/microcontroller, the pins should be added
|
||||
here. Refer to the instructions on the respective extensions page on how to add
|
||||
them. If not adding any extensions, leave this as an empty list as shown.
|
||||
|
||||
# Coord mapping
|
||||
If your keyboard is not built electrically as a square (though most are), you can
|
||||
provide a mapping directly. An example of this is the
|
||||
[Corne](https://github.com/foostan/crkbd). That has 12 columns for 3 rows, and 6
|
||||
columns for the bottom row. Split keyboards count as the total keyboard, not per
|
||||
side, the right side being offset by the number of keys on the left side, as if
|
||||
the rows were stacked.
|
||||
That would look like this
|
||||
```python
|
||||
from kmk.scanners import intify_coordinate as ic
|
||||
|
||||
coord_mapping = []
|
||||
coord_mapping.extend(ic(0, x, 6) for x in range(6))
|
||||
coord_mapping.extend(ic(4, x, 6) for x in range(6))
|
||||
coord_mapping.extend(ic(1, x, 6) for x in range(6))
|
||||
coord_mapping.extend(ic(5, x, 6) for x in range(6))
|
||||
coord_mapping.extend(ic(2, x, 6) for x in range(6))
|
||||
coord_mapping.extend(ic(6, x, 6) for x in range(6))
|
||||
# And now, to handle R3, which at this point is down to just six keys
|
||||
coord_mapping.extend(ic(3, x, 6) for x in range(3, 6))
|
||||
coord_mapping.extend(ic(7, x, 6) for x in range(0, 3))
|
||||
```
|
||||
|
||||
`intify_coordinate` is the traditional way to generate key positions.
|
||||
Here's an equivalent, maybe visually more explanatory version:
|
||||
```python
|
||||
coord_mapping = [
|
||||
0, 1, 2, 3, 4, 5, 24, 25, 26, 27, 28, 29,
|
||||
6, 7, 8, 9, 10, 11, 30, 31, 32, 33, 34, 35,
|
||||
12, 13, 14, 15, 16, 17, 36, 37, 38, 39, 40, 41,
|
||||
21, 22, 23, 42, 43, 44,
|
||||
]
|
||||
```
|
||||
|
||||
Note: Not all numbers are necessarily used ! The keyboard assumes
|
||||
`number of line * number of rows` keys. Some of the possible keys might not be
|
||||
used. For example a keyboard with 60 keys might have 8 rows, 8 cols, allowing
|
||||
64 total combinations -- hence 64 keys. 4 numbers will then not be used for keys
|
||||
in the `coord_mapping` (might be anyone of them depending of the wiring).
|
||||
|
||||
### Find your coord mapping
|
||||
The following code will help you setup your `coord_mapping` by having every key
|
||||
send its corresponding number. Use it after your pins and module definition
|
||||
to define both `keyboard.coord_mapping` and `keyboard.keymap`.
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
from kmk.keys import KC
|
||||
|
||||
# *2 for split keyboards, which will typically manage twice the number of keys
|
||||
# of one side. Having this N too large will have no impact (maybe slower boot..)
|
||||
N = len(keyboard.col_pins) * len(keyboard.row_pins) * 2
|
||||
|
||||
keyboard.coord_mapping = list(range(N))
|
||||
|
||||
layer = []
|
||||
|
||||
for i in range(N):
|
||||
c, r = divmod(i, 100)
|
||||
d, u = divmod(r, 10)
|
||||
layer.append(
|
||||
simple_key_sequence(
|
||||
(
|
||||
getattr(KC, 'N' + str(c)),
|
||||
getattr(KC, 'N' + str(d)),
|
||||
getattr(KC, 'N' + str(u)),
|
||||
KC.SPC,
|
||||
)
|
||||
)
|
||||
)
|
||||
keyboard.keymap = [layer]
|
||||
|
||||
if __name__ == '__main__':
|
||||
keyboard.go()
|
||||
```
|
||||
|
||||
## Keymaps
|
||||
Keymaps are organized as a list of lists. Keycodes are added for every key on
|
||||
each layer. See [keycodes](keycodes.md) for more details on what keycodes are
|
||||
available. If using layers or other extensions, also refer to the extensions
|
||||
page for additional keycodes.
|
||||
```python
|
||||
from kb import KMKKeyboard
|
||||
from kmk.keys import KC
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
|
||||
keyboard.keymap = [
|
||||
[KC.A, KC.B],
|
||||
[KC.C, KC.D],
|
||||
]
|
||||
|
||||
if __name__ == '__main__':
|
||||
keyboard.go()
|
||||
```
|
||||
|
||||
## More information
|
||||
More information on keymaps can be found [here](config_and_keymap.md)
|
42
docs/en/power.md
Normal file
42
docs/en/power.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Power(save)
|
||||
This module allows you to save power and is targeted to Bluetooth/battery
|
||||
based keyboards.
|
||||
|
||||
## Keycodes
|
||||
|Key |Description |
|
||||
|-----------------------|-------------------------|
|
||||
|`KC.PS_TOG ` |Toggles powersave on/off |
|
||||
|`KC.PS_ON ` |Turns powersave on |
|
||||
|`KC.PS_OFF ` |Turns powersave off |
|
||||
|
||||
# Enabling the extension
|
||||
To turn on basic power saving, this is all that is required.
|
||||
```python
|
||||
from kmk.modules.power import Power
|
||||
|
||||
power = Power()
|
||||
|
||||
keyboard.modules.append(power)
|
||||
|
||||
```
|
||||
|
||||
## Optional extra power saving
|
||||
On supported boards, such as the nice!nano, power can be cut on VCC saving extra
|
||||
power if OLEDS or RGBs are installed. These drain power even when off, so this
|
||||
will prevent them from doing so.
|
||||
|
||||
```python
|
||||
from kmk.modules.power import Power
|
||||
|
||||
# Your kb.py may already have this set. If not, add it like this
|
||||
# import board
|
||||
# keyboard.powersave_pin = board.P0_13
|
||||
power = Power(powersave_pin=keyboard.powersave_pin)
|
||||
|
||||
keyboard.modules.append(power)
|
||||
|
||||
```
|
||||
|
||||
Make sure that the pin is correct for your microcontroller. The example is for
|
||||
the nice!nano. Not all microcontrollers have this feature and this can be omitted
|
||||
if not and there will simply be less power saving.
|
50
docs/en/rapidfire.md
Normal file
50
docs/en/rapidfire.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# RapidFire
|
||||
|
||||
The RapidFire module lets a user send repeated key taps while a key is held.
|
||||
|
||||
Some instances where this may be useful are:
|
||||
|
||||
- MMOs and other games where you are encouraged to repeatedly spam a key
|
||||
- More responsive volume up and volume down
|
||||
- Faster cursor key navigation
|
||||
- Combine with the [Mouse Keys](https://github.com/KMKfw/kmk_firmware/blob/master/docs/mouse_keys.md) module to create rapid-fire mouse clicks
|
||||
- Anywhere else you may need an ergonomic alternative to repetitive key tapping
|
||||
|
||||
## Keycodes
|
||||
|
||||
| Key | Description |
|
||||
| :---------- | :--------------------------------------------------- |
|
||||
| `KC.RF(kc)` | Repeatedly sends the specified keycode while pressed |
|
||||
|
||||
## Usage
|
||||
|
||||
Each repeat counts as one full cycle of pressing and releasing. RapidFire works with chording (i.e., holding Shift plus a RapidFire key will repeatedly send the shifted version of that RapidFire key) and chaining (i.e., `KC.RF(KC.LSHIFT(KC.A))`. Multiple RapidFire keys can be held down at the same time, and their timers work independently of each other.
|
||||
|
||||
The RapidFire keycode has a few different options:
|
||||
|
||||
| Option | Default Value | Description |
|
||||
| :-----------------------------: | :-----------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `interval` | `100` | The time between key taps sent in milliseconds. Note: `10` appears to be the minimum effective value. |
|
||||
| `timeout` | `200` | The amount of time in milliseconds the key must be held down before RapidFire activates. Useful if you want to be able to type with keys that have a low `interval` value. A value of `0` will result in no waiting period. |
|
||||
| `enable_interval_randomization` | `False` | Enable randomizing the value of `interval`. Useful for making the repetitive input look human in instances where you may be flagged as a bot otherwise. |
|
||||
| `randomization_magnitude` | `15` | If `enable_interval_randomization` is `True`, the time between key taps sent will be `interval` plus or minus a random value up to this amount. |
|
||||
| `toggle` | `False` | If set to `True`, activating RapidFire will toggle it on or off. Useful if you don't want to have to keep the button held. Set `timeout` to `0` if you would like to toggle on tap. |
|
||||
|
||||
### Example Code
|
||||
|
||||
```python
|
||||
from kmk.modules.rapidfire import RapidFire
|
||||
|
||||
keyboard.modules.append(RapidFire())
|
||||
|
||||
# After 200 milliseconds, repeatedly send Shift+A every 75-125 milliseconds while the button is held
|
||||
SPAM_A = KC.RF(KC.LSFT(KC.A), timeout=200, interval=100, enable_interval_randomization=True, randomization_magnitude=25)
|
||||
# Immediately toggle repeatedly sending Enter every 50 milliseconds on tap
|
||||
SPAM_ENTER = KC.RF(KC.ENT, toggle=True, timeout=0, interval=50)
|
||||
|
||||
|
||||
keyboard.keymap = [[
|
||||
SPAM_A, SPAM_ENTER
|
||||
]]
|
||||
|
||||
```
|
187
docs/en/rgb.md
Normal file
187
docs/en/rgb.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# RGB/Underglow/NeoPixel
|
||||
Want your keyboard to shine? Add some lights!
|
||||
|
||||
## CircuitPython
|
||||
This does require the [NeoPixel library from Adafruit](https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel/blob/main/neopixel.py).
|
||||
It is part of the [Adafruit CircuitPython Bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle).
|
||||
Simply put this in the "root" of your CircuitPython device. If unsure, it's the folder with `main.py` in it, and should be the first folder you see when you open the device.
|
||||
|
||||
Currently we support the following addressable LEDs:
|
||||
|
||||
* WS2811, WS2812, WS2812B, WS2812C, etc.
|
||||
* SK6812, SK6812MINI, SK6805
|
||||
|
||||
### Color Selection
|
||||
|
||||
KMK uses [Hue, Saturation, and Value](https://en.wikipedia.org/wiki/HSL_and_HSV) to select colors rather than RGB. The color wheel below demonstrates how this works.
|
||||
|
||||
Changing the **Hue** cycles around the circle.
|
||||
Changing the **Saturation** moves between the inner and outer sections of the wheel, affecting the intensity of the color.
|
||||
Changing the **Value** sets the overall brightness.
|
||||
|
||||
## Enabling the extension
|
||||
The only required values that you need to give the RGB extension would be the board pin for the data line, and the number of pixels/LED's. If using a split keyboard, this number is per side, and not the total of both sides.
|
||||
```python
|
||||
import board
|
||||
from kmk.extensions.RGB import RGB
|
||||
|
||||
rgb = RGB(pixel_pin=board.GP14, num_pixels=27)
|
||||
keyboard.extensions.append(rgb)
|
||||
```
|
||||
|
||||
## [Keycodes]
|
||||
|
||||
|Key |Aliases |Description |
|
||||
|-----------------------------|-------------------|----------------------------|
|
||||
|`KC.RGB_TOG` | |Toggles RGB |
|
||||
|`KC.RGB_HUI` | |Increase Hue |
|
||||
|`KC.RGB_HUD` | |Decrease Hue |
|
||||
|`KC.RGB_SAI` | |Increase Saturation |
|
||||
|`KC.RGB_SAD` | |Decrease Saturation |
|
||||
|`KC.RGB_VAI` | |Increase Value |
|
||||
|`KC.RGB_VAD` | |Decrease Value |
|
||||
|`KC.RGB_ANI` | |Increase animation speed |
|
||||
|`KC.RGB_AND` | |Decrease animation speed |
|
||||
|`KC.RGB_MODE_PLAIN` |`RGB_M_P` |Static RGB |
|
||||
|`KC.RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation |
|
||||
|`KC.RGB_MODE_RAINBOW` |`RGB_M_R` |Rainbow animation |
|
||||
|`KC.RGB_MODE_BREATHE_RAINBOW`|`RGB_M_BR` |Breathing rainbow animation |
|
||||
|`KC.RGB_MODE_KNIGHT` |`RGB_M_K` |Knight Rider animation |
|
||||
|`KC.RGB_MODE_SWIRL` |`RGB_M_S` |Swirl animation |
|
||||
|
||||
## Configuration
|
||||
|Define |Default |Description |
|
||||
|-------------------------------------|-------------|-----------------------------------------------------------------------------|
|
||||
|`rgb.num_pixels`| |The number of LEDs connected |
|
||||
|`rgb.rgb_order` |`(1, 0, 2)` |The order of the pixels R G B, and optionally white. Example(1, 0, 2, 3) |
|
||||
|`rgb.hue_step` |`10` |The number of steps to cycle through the hue by |
|
||||
|`rgb.sat_step` |`17` |The number of steps to change the saturation by |
|
||||
|`rgb.val_step` |`17` |The number of steps to change the brightness by |
|
||||
|`rgb.hue_default` |`0` |The default hue when the keyboard boots |
|
||||
|`rgb.sat_default` |`255` |The default saturation when the keyboard boots |
|
||||
|`rgb.val_default` |`255` |The default value (brightness) when the keyboard boots |
|
||||
|`rgb.val_limit` |`255` |The maximum brightness level |
|
||||
|
||||
## Built-in Animation Configuration
|
||||
|Define |Default |Description |
|
||||
|----------------------------------------------|-------------|-------------------------------------------------------------------------------------|
|
||||
|`rgb.breathe_center` |`1.5` |Used to calculate the curve for the breathing animation. Anywhere from 1.0 - 2.7 is valid|
|
||||
|`rgb.knight_effect_length` |`4` |The number of LEDs to light up for the "Knight" animation |
|
||||
|
||||
## Functions
|
||||
|
||||
If you want to create your own animations, or for example, change the lighting in a macro, or a layer switch, here are some functions that are available.
|
||||
|
||||
|Function |Description |
|
||||
|--------------------------------------------------|--------------------------------------------------------------------------------------------|
|
||||
|`rgb.set_hsv_fill(hue, sat, val)` |Fills all LED's with HSV values |
|
||||
|`rgb.set_hsv(hue, sat, val, index)` |Sets a single LED with HSV value |
|
||||
|`rgb.set_rgb_fill((r, g, b))` |Fills all LED's with RGB(W) values |
|
||||
|`rgb.set_rgb((r, g, b), index)` |Set's a single LED with RGB(W) values |
|
||||
|`rgb.disable_auto_write(bool)` |When True, disables showing changes. Good for setting multiple LED's before a visible update|
|
||||
|`rgb.increase_hue(step)` |Increases hue by a given step |
|
||||
|`rgb.decrease_hue(step)` |Decreases hue by a given step |
|
||||
|`rgb.increase_sat(step)` |Increases saturation by a given step |
|
||||
|`rgb.decrease_sat(step)` |Decreases saturation by a given step |
|
||||
|`rgb.increase_val(step)` |Increases value (brightness) by a given step |
|
||||
|`rgb.decrease_val(step)` |Decreases value (brightness) by a given step |
|
||||
|`rgb.increase_ani()` |Increases animation speed by 1. Maximum 10 |
|
||||
|`rgb.decrease_ani()` |Decreases animation speed by 1. Minimum 10 |
|
||||
|`rgb.off()` |Turns all LED's off |
|
||||
|`rgb.show()` |Displays all stored configuration for LED's. Useful with disable_auto_write explained above |
|
||||
|
||||
## Direct variable access
|
||||
|Define |Default |Description |
|
||||
|-----------------------------------|-----------|-----------------------------------------------------------------------------------------------------------|
|
||||
|`rgb.hue` |`0` |Sets the hue from 0-255 |
|
||||
|`rgb.sat` |`255` |Sets the saturation from 0-255 |
|
||||
|`rgb.val` |`255` |Sets the brightness from 0-255 |
|
||||
|`rgb.reverse_animation`|`False` |If true, some animations will run in reverse. Can be safely used in user animations |
|
||||
|`rgb.animation_mode` |`static` |This can be changed to any modes included, or to something custom for user animations. Any string is valid |
|
||||
|`rgb.animation_speed` |`1` |Increases animation speed of most animations. Recommended 1-5, Maximum 10. |
|
||||
|
||||
```python
|
||||
from kmk.extensions.rgb import AnimationModes
|
||||
rgb = RGB(pixel_pin=rgb_pixel_pin,
|
||||
num_pixels=27
|
||||
val_limit=100,
|
||||
hue_default=0,
|
||||
sat_default=100,
|
||||
rgb_order=(1, 0, 2), # GRB WS2812
|
||||
val_default=100,
|
||||
hue_step=5,
|
||||
sat_step=5,
|
||||
val_step=5,
|
||||
animation_speed=1,
|
||||
breathe_center=1, # 1.0-2.7
|
||||
knight_effect_length=3,
|
||||
animation_mode=AnimationModes.STATIC,
|
||||
reverse_animation=False,
|
||||
refresh_rate=60,
|
||||
)
|
||||
```
|
||||
|
||||
## Hardware Modification
|
||||
|
||||
To add RGB LED's to boards that don't support them directly, you will have to
|
||||
add a 3 wires. The power wire will run on 3.3v or 5v (depending on the LED),
|
||||
ground, and data pins will need added to an unused pin on your microcontroller
|
||||
unless your keyboard has specific solder points for them. With those 3 wires
|
||||
connected, set the `pixel_pin` as described above, and you are ready to use your
|
||||
RGB LED's/NeoPixel.
|
||||
|
||||
## Troubleshooting
|
||||
### Incorrect colors
|
||||
If your colors are incorrect, check the pixel order of your specific LED's. Here are some common ones.
|
||||
* WS2811, WS2812, WS2812B, WS2812C are all GRB (1, 0, 2)
|
||||
* SK6812, SK6812MINI, SK6805 are all GRB (1, 0, 2)
|
||||
* NeoPixels will vary depending on which one you buy. It will be listed on the product page.
|
||||
|
||||
### Lights don't turn on
|
||||
|
||||
Make sure that your board supports LED backlight by checking for a line with
|
||||
`PIXEL_PIN`. If it does not, you can add it to your keymap. If you added the
|
||||
LED's yourself, you will also need to set `num_pixels` to the number of
|
||||
installed LED's in total.
|
||||
|
||||
|
||||
## Alternate LED chipsets
|
||||
|
||||
Not all RGB LEDs are compatible with NeoPixels. To support these, the RGB
|
||||
extension accepts an instance of a `Pixelbuf`-compatible object as an optional
|
||||
parameter. If supplied, `pixel_pin` is ignored and the supplied Pixelbuf is
|
||||
used instead of creating a NeoPixel object.
|
||||
The RGB extension will figure out LED count from the pixelbuffer length if not
|
||||
passed explicitly.
|
||||
|
||||
This works easily with APA102 ("DotStar") LEDs, but for most other RGB LED
|
||||
chipsets you will need to provide a wrapper to match the expected interface.
|
||||
|
||||
A simple example using APA102:
|
||||
```python
|
||||
import adafruit_dotstar
|
||||
from kmk.extensions.RGB import RGB
|
||||
from kb import rgb_pixel_pin # This can be imported or defined manually
|
||||
|
||||
_LED_COUNT=12
|
||||
pixels = adafruit_dotstar.DotStar(board.SCK, board.MOSI, _LED_COUNT)
|
||||
|
||||
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb)
|
||||
```
|
||||
|
||||
### Multiple PixelBuffer
|
||||
Similar to alternate drivers, the RGB module supports passing multiple `Pixelbuf`
|
||||
objects as an iterable.
|
||||
```python
|
||||
from kmk.extensions.RGB import RGB
|
||||
|
||||
pixels = (
|
||||
Neopixel(...),
|
||||
DotStar(...),
|
||||
CustomPixelBuf(...)
|
||||
)
|
||||
|
||||
rgb = RGB(pixel_pin=None, pixels=pixels)
|
||||
keyboard.extensions.append(rgb)
|
||||
```
|
207
docs/en/scanners.md
Normal file
207
docs/en/scanners.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Scanners
|
||||
|
||||
The default key scanner in KMK assumes a garden variety switch matrix, with
|
||||
one diode per switch to prevent ghosting.
|
||||
This doesn't cover all hardware designs though. With macro pads, for example, it
|
||||
is very common to not have a matrix topology at all.
|
||||
Boards like this aren't compatible with the default matrix scanner, so you will
|
||||
need to swap it out with an alternative scanner.
|
||||
|
||||
|
||||
## Keypad Scanners
|
||||
The scanners in `kmk.scanners.keypad` wrap the `keypad` module that ships with
|
||||
CircuitPython and support the same configuration and tuning options as their
|
||||
upstream. You can find out more in the [CircuitPython
|
||||
documentation](https://docs.circuitpython.org/en/latest/shared-bindings/keypad/index.html).
|
||||
|
||||
### keypad MatrixScanner
|
||||
This is the default scanner used by KMK.
|
||||
It uses the CircuitPython builtin `keypad.KeyMatrix`.
|
||||
|
||||
```python
|
||||
from kmk.scanners.keypad import MatrixScanner
|
||||
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = MatrixScanner(
|
||||
# required arguments:
|
||||
column_pins=self.col_pins,
|
||||
row_pins=self.row_pins,
|
||||
# optional arguments with defaults:
|
||||
columns_to_anodes=DiodeOrientation.COL2ROW,
|
||||
interval=0.02,
|
||||
max_events=64
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
|
||||
### keypad KeysScanner
|
||||
|
||||
The `keypad.Keys` scanner treats individual GPIO pins as discrete keys. To use
|
||||
this scanner, provide a sequence of pins that describes the layout of your
|
||||
board then include it in the initialization sequence of your keyboard class.
|
||||
|
||||
```python
|
||||
import board
|
||||
from kmk.kmk_keyboard import KMKKeyboard
|
||||
from kmk.scanners.keypad import KeysScanner
|
||||
|
||||
|
||||
# GPIO to key mapping - each line is a new row.
|
||||
_KEY_CFG = [
|
||||
board.SW3, board.SW7, board.SW11, board.SW15,
|
||||
board.SW2, board.SW6, board.SW10, board.SW14,
|
||||
board.SW1, board.SW5, board.SW9, board.SW13,
|
||||
board.SW0, board.SW4, board.SW8, board.SW12,
|
||||
]
|
||||
|
||||
|
||||
# Keyboard implementation class
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = KeysScanner(
|
||||
# require argument:
|
||||
pins=_KEY_CFG,
|
||||
# optional arguments with defaults:
|
||||
value_when_pressed=False,
|
||||
pull=True,
|
||||
interval=0.02,
|
||||
max_events=64
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
### keypad ShiftRegisterKeys
|
||||
|
||||
This scanner can read keys attached to a parallel-in serial-out shift register
|
||||
like the 74HC165 or CD4021. Note that you may chain shift registers to load in
|
||||
as many values as you need.
|
||||
```python
|
||||
from kmk.scanners.keypad import ShiftRegisterKeys
|
||||
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = ShiftRegisterKeys(
|
||||
# require arguments:
|
||||
clock=board.GP0,
|
||||
data=board.GP1,
|
||||
latch=board.GP2,
|
||||
key_count=8,
|
||||
# optional arguments with defaults:
|
||||
value_to_latch=True, # 74HC165: True, CD4021: False
|
||||
value_when_pressed=False,
|
||||
interval=0.02,
|
||||
max_events=64
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Digitalio Scanners
|
||||
|
||||
### digitalio MatrixScanner
|
||||
|
||||
The digitalio Matrix can scan over, as the name implies, `digitalio.DigitalInOut`
|
||||
objects. That is especially useful if a matrix is build with IO-expanders.
|
||||
|
||||
```python
|
||||
from kmk.scanners.digitalio import MatrixScanner
|
||||
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = MatrixScanner(
|
||||
cols=self.col_pins,
|
||||
rows=self.row_pins,
|
||||
diode_orientation=self.diode_orientation,
|
||||
rollover_cols_every_rows=None, # optional
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Rotary Encoder Scanners
|
||||
|
||||
### RotaryioEncoder
|
||||
|
||||
Matrix events from a quadrature ("rotary") encoder?
|
||||
|
||||
```python
|
||||
from kmk.scanners.encoder import RotaryioEncoder
|
||||
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
def __init__(self):
|
||||
# create and register the scanner
|
||||
self.matrix = RotaryioEncoder(
|
||||
pin_a=board.GP0,
|
||||
pin_b=board.GP1,
|
||||
# optional
|
||||
divisor=4,
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## `Scanner` base class
|
||||
|
||||
If you require a different type of scanner, you can create your own by
|
||||
providing a subclass of `Scanner`. This is a very simple interface, it only
|
||||
contains a single method, `scan_for_changes(self)` which returns a key report
|
||||
if one exists, or `None` otherwise.
|
||||
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
### Multiple Scanners
|
||||
|
||||
Sometimes a single scanner doesn't cover all hardware configurations. For
|
||||
example: The bulk of the keyboard may be scanned with a matrix scanner, but a
|
||||
couple of additional keys are directly connected to GPIOs.
|
||||
In that case KMK allows you to define multiple scanners. The `KMKKeyboard.matrix` attribute can either be assigned a single scanner, or a list of scanners.
|
||||
KMK assumes that successive scanner keys are consecutive, and populates
|
||||
`KMKKeyboard.coord_mapping` accordingly; for convenience you may have to supply a `coord_mapping` that resembles your physical layout more closely (expanded below).
|
||||
|
||||
Example:
|
||||
```python
|
||||
class MyKeyboard(KMKKeyboard):
|
||||
self.matrix = [
|
||||
MatrixScanner(...),
|
||||
KeysScanner(...),
|
||||
# etc...
|
||||
]
|
||||
```
|
||||
#### Multiple Scanners coord_mapping and keymap changes
|
||||
To add more scanners you need to add onto your `coord_mapping`.
|
||||
|
||||
Example:
|
||||
|
||||
`coord_mapping` with just one `MatrixScanner` on a 58 key split keyboard:
|
||||
```python
|
||||
coord_mapping = [
|
||||
0, 1, 2, 3, 4, 5, 35, 34, 33, 32, 31, 30,
|
||||
6, 7, 8, 9, 10, 11, 41, 40, 39, 38, 37, 36,
|
||||
12, 13, 14, 15, 16, 17, 47, 46, 45, 44, 43, 42,
|
||||
18, 19, 20, 21, 22, 23, 29, 59, 53, 52, 51, 50, 49, 48,
|
||||
25, 26, 27, 28, 58, 57, 56, 55,
|
||||
]
|
||||
```
|
||||
|
||||
`coord_mapping` using `MatrixScanner` and `RotaryioEncoder` on the same 58 key split keyboard with an encoder on each half:
|
||||
```python
|
||||
coord_mapping = [
|
||||
0, 1, 2, 3, 4, 5, 37, 36, 35, 34, 33, 32,
|
||||
6, 7, 8, 9, 10, 11, 43, 42, 41, 40, 39, 38,
|
||||
12, 13, 14, 15, 16, 17, 49, 48, 47, 46, 45, 44,
|
||||
18, 19, 20, 21, 22, 23, 29, 61, 55, 54, 53, 52, 51, 50,
|
||||
25, 26, 27, 28, 60, 59, 58, 57,
|
||||
30, 31, 62, 63
|
||||
]
|
||||
```
|
||||
|
||||
On the top left side of a standard split keyboard `coord_mapping`, right below that you see a split keyboard where `RotaryioEncoder` and `MatrixScanner` (the default scanner) are used.
|
||||
In the single scanner example, we used to count from 0 to 29 while the top right side starts at 30.
|
||||
With the addition of the encoder scanner, the left side has 2 additional keys making it count up to 31 and the right side would then start at 32 and count to 63.
|
||||
This means that keys 30, 31, 62, and 63 are for encoders.
|
||||
Notice that all of the encoders are at the end of the array, because we put the encoder scanner after the matrix scanner in `keyboard.matrix`.
|
||||
Therefore, we need to add 4 more key codes in the corresponding places of our `keyboard.keymap`, they will be used for the encoders.
|
185
docs/en/sequences.md
Normal file
185
docs/en/sequences.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Sequences
|
||||
|
||||
Sequences are used for sending multiple keystrokes in a single action, and can
|
||||
be used for things like Unicode characters (even emojis! 🇨🇦), _Lorem ipsum_
|
||||
generators, triggering side effects (think lighting, speakers,
|
||||
microcontroller-optimized cryptocurrency miners, whatever). If you are still
|
||||
unsure of what this is, most other vendors call these "Macros", but can do much
|
||||
more if you wish.
|
||||
|
||||
## Sending strings
|
||||
The most basic sequence is `send_string`. It can be used to send any standard
|
||||
English alphabet character, and an assortment of other "standard" keyboard keys
|
||||
(return, space, exclamation points, etc.)
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import send_string
|
||||
|
||||
WOW = send_string("Wow, KMK is awesome!")
|
||||
|
||||
keyboard.keymap = [<other keycodes>, WOW, <other keycodes>]
|
||||
```
|
||||
|
||||
## Key sequences
|
||||
If you need to add modifier keys to your sequence, instead of `send_string` use
|
||||
`simple_key_sequence`. While it's not as visually clean as `send_string`, you can
|
||||
use it to add things like copying/pasting, tabbing between fields, etc.
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
PASTE_WITH_COMMENTARY = simple_key_sequence(
|
||||
(
|
||||
KC.L,
|
||||
KC.O,
|
||||
KC.O,
|
||||
KC.K,
|
||||
KC.SPC,
|
||||
KC.A,
|
||||
KC.T,
|
||||
KC.SPC,
|
||||
KC.T,
|
||||
KC.H,
|
||||
KC.I,
|
||||
KC.S,
|
||||
KC.COLN,
|
||||
KC.SPC,
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
)
|
||||
|
||||
keyboard.keymap = [<other keycodes>, PASTE_WITH_COMMENTARY, <other keycodes>]
|
||||
```
|
||||
|
||||
The above example will type out "look at this: " and then paste the contents of your
|
||||
clipboard.
|
||||
|
||||
|
||||
### Sleeping within a sequence
|
||||
|
||||
If you need to wait during a sequence, you can use `KC.MACRO_SLEEP_MS(ms)` to wait a
|
||||
length of time, in milliseconds.
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
COUNTDOWN_TO_PASTE = simple_key_sequence(
|
||||
(
|
||||
KC.N3,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N2,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP_MS(1000),
|
||||
KC.N1,
|
||||
KC.ENTER,
|
||||
KC.MACRO_SLEEP(1000),
|
||||
KC.LCTL(KC.V),
|
||||
)
|
||||
)
|
||||
|
||||
keyboard.keymap = [<other keycodes>, COUNTDOWN_TO_PASTE, <other keycodes>]
|
||||
```
|
||||
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
NEXT = simple_key_sequence(
|
||||
(
|
||||
KC.LALT(no_release=True),
|
||||
KC.MACRO_SLEEP_MS(30),
|
||||
KC.TAB,
|
||||
KC.MACRO_SLEEP_MS(30),
|
||||
KC.LALT(no_press=True),
|
||||
)
|
||||
)
|
||||
|
||||
This example will type out the following, waiting one second (1000 ms) between numbers:
|
||||
|
||||
3
|
||||
2
|
||||
1
|
||||
|
||||
and then paste the contents of your clipboard.
|
||||
|
||||
### Alt Tab with delay
|
||||
|
||||
If alt tab isn't working because it requires a delay, adding a delay and triggering
|
||||
down and up on ALT manually may fix the issue.
|
||||
|
||||
``` python
|
||||
from kmk.handlers.sequences import simple_key_sequence
|
||||
|
||||
NEXT = simple_key_sequence(
|
||||
(
|
||||
KC.LALT(no_release=True),
|
||||
KC.MACRO_SLEEP_MS(30),
|
||||
KC.TAB,
|
||||
KC.MACRO_SLEEP_MS(30),
|
||||
KC.LALT(no_press=True),
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Unicode
|
||||
Before trying to send Unicode sequences, make sure you set your `UnicodeMode`.
|
||||
You can set an initial value in your keymap by setting `keyboard.unicode_mode`.
|
||||
|
||||
Keys are provided to change this mode at runtime - for example, `KC.UC_MODE_LINUX`.
|
||||
|
||||
|
||||
### Unicode Modes:
|
||||
On Linux, Unicode uses `Ctrl-Shift-U`, which is supported by `ibus` and GTK+3.
|
||||
`ibus` users will need to add `IBUS_ENABLE_CTRL_SHIFT_U=1` to their environment
|
||||
(`~/profile`, `~/.bashrc`, `~/.zshrc`, or through your desktop environment's
|
||||
configurator).
|
||||
|
||||
On Windows, [WinCompose](https://github.com/samhocevar/wincompose) is required.
|
||||
|
||||
- Linux : `UnicodeMode.LINUX` or `UnicodeMode.IBUS`
|
||||
- Mac: `UnicodeMode.MACOS` or `UnicodeMode.OSX` or `UnicodeMode.RALT`
|
||||
- Windows: `UnicodeMode.WINC`
|
||||
|
||||
|
||||
### Unicode Examples
|
||||
|
||||
To send a simple Unicode symbol
|
||||
```python
|
||||
from kmk.handlers.sequences import unicode_string_sequence
|
||||
|
||||
FLIP = unicode_string_sequence('(ノಠ痊ಠ)ノ彡┻━┻')
|
||||
|
||||
keyboard.keymap = [<other keycodes>, FLIP, <other keycodes>]
|
||||
```
|
||||
|
||||
If you'd rather keep a lookup table of your sequences (perhaps to bind emojis to
|
||||
keys), that's supported too, through an obnoxiously long-winded method:
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import compile_unicode_string_sequences as cuss
|
||||
|
||||
emoticons = cuss({
|
||||
'BEER': r'🍺',
|
||||
'HAND_WAVE': r'👋',
|
||||
})
|
||||
|
||||
keymap = [<other keycodes>, emoticons.BEER, emoticons.HAND_WAVE, <other keycodes>]
|
||||
```
|
||||
|
||||
> The observant will notice dot-notation is supported here despite feeding in a
|
||||
> dictionary - the return of `compile_unicode_string_sequences` is a
|
||||
> `kmk.types.AttrDict`, which you can think of as a read-only view over a
|
||||
> dictionary adding attribute-based (dot-notation) access.
|
||||
|
||||
Finally, if you need to send arbitrary Unicode codepoints in raw form, that's
|
||||
supported too, through `unicode_codepoint_sequence`.
|
||||
|
||||
```python
|
||||
from kmk.handlers.sequences import unicode_codepoint_sequence
|
||||
|
||||
TABLE_FLIP = unicode_codepoint_sequence([
|
||||
"28", "30ce", "ca0", "75ca","ca0", "29",
|
||||
"30ce", "5f61", "253b", "2501", "253b",
|
||||
])
|
||||
|
||||
keyboard.keymap = [<other keycodes>, TABLE_FLIP, <other keycodes>]
|
||||
```
|
51
docs/en/serialace.md
Normal file
51
docs/en/serialace.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Serial ACE (Arbitrary Code Execution over serial interface)
|
||||
|
||||
**Caution: This module allows unrestricted, arbitrary code execution on your KMK
|
||||
device. That includes potential exploits, such as keyloggers, and unvetted
|
||||
user code that may result in undesired behaviour and/or crashes.
|
||||
This feature is purely experimental in the sense that you probably neither
|
||||
want nor should use it in production.
|
||||
Advanced knowledge of python and the serial console is required, and we will
|
||||
not provide help or support in any way.**
|
||||
|
||||
This module provides an API to run any valid python code on your keyboard and
|
||||
return the result of that code via an additional serial consol (not the one you
|
||||
use for the circuitpython debugger).
|
||||
|
||||
|
||||
## Prerequisite
|
||||
|
||||
Enable the data serial in `boot.py`:
|
||||
```python
|
||||
import usb_cdc
|
||||
usb_cdc.enable(data=True)
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
Enable the module, just as any other module else:
|
||||
```
|
||||
from kmk.modules.serialace import SerialACE
|
||||
keyboard.modules.append(SerialACE())
|
||||
```
|
||||
|
||||
Assume the data serial is on `/dev/ttyACM1`.
|
||||
Depending on your OS settings, it bay be necessary to explicitly set the serial
|
||||
device to raw transmission, no echo:
|
||||
```bash
|
||||
stty -F /dev/ttyACM1 raw -echo
|
||||
```
|
||||
|
||||
### Get the List of Active Layers
|
||||
```bash
|
||||
$ echo "keyboard.active_layers" > /dev/ttyACM1
|
||||
$ cat /dev/ttyACM1
|
||||
[0]
|
||||
```
|
||||
|
||||
### "Tap" a Key
|
||||
```bash
|
||||
$ echo "exec('from kmk.keys import KC; keyboard.tap_key(KC.Y)')" > /dev/ttyACM1
|
||||
$ y
|
||||
```
|
126
docs/en/split_keyboards.md
Normal file
126
docs/en/split_keyboards.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Split Keyboards
|
||||
Split keyboards are mostly the same as unsplit. Wired UART is fully supported,
|
||||
and testing of Bluetooth splits, though we don't currently offer support for this.
|
||||
|
||||
Notice that this Split module must be added after the ModTap module to the keyboard.modules.
|
||||
|
||||
## Drive names
|
||||
As you will have two circuitpython drives to update regularly, it is adviced to rename them to make
|
||||
your life easier. Follow the instructions on how to [rename circuitpydrives](https://learn.adafruit.com/welcome-to-circuitpython/renaming-circuitpy) while making sure that:
|
||||
- The left side ends in "L",
|
||||
- the right side ends in "R",
|
||||
- the entire drive name is 11 characters or less! This is a limitation of the filesystem and you will receive an error if you choose a name longer than that.
|
||||
|
||||
For example: `NYQUISTL` for the left and `NYQUISTR` for the right half.
|
||||
|
||||
## Wired UART
|
||||
Wired connections can use UART over 1 or 2 wires. With 2 wires, you will be able
|
||||
to synchronize the halves allowing additional features in some extensions.
|
||||
```python
|
||||
from kb import data_pin
|
||||
from kmk.modules.split import Split, SplitSide
|
||||
|
||||
split = Split(split_side=SplitSide.LEFT)
|
||||
keyboard.modules.append(split)
|
||||
```
|
||||
|
||||
## Bluetooth split (aka no TRRS) [Currently in testing]
|
||||
Wireless splits are fully featured with 2 way communication allowing all extensions to work 100%.
|
||||
```python
|
||||
from kb import data_pin
|
||||
from kmk.modules.split import Split, SplitType, SplitSide
|
||||
|
||||
|
||||
split = Split(split_type=SplitType.BLE, split_side=SplitSide.LEFT)
|
||||
OR
|
||||
split = Split(split_type=SplitType.BLE, split_side=SplitSide.RIGHT)
|
||||
keyboard.modules.append(split)
|
||||
```
|
||||
|
||||
## Config
|
||||
Useful config options:
|
||||
```python
|
||||
split = Split(
|
||||
split_flip=True, # If both halves are the same, but flipped, set this True
|
||||
split_side=None, # Sets if this is to SplitSide.LEFT or SplitSide.RIGHT, or use EE hands
|
||||
split_type=SplitType.UART, # Defaults to UART
|
||||
split_target_left=True, # Assumes that left will be the one on USB. Set to False if it will be the right
|
||||
uart_interval=20, # Sets the uarts delay. Lower numbers draw more power
|
||||
data_pin=None, # The primary data pin to talk to the secondary device with
|
||||
data_pin2=None, # Second uart pin to allow 2 way communication
|
||||
uart_flip=True, # Reverses the RX and TX pins if both are provided
|
||||
use_pio=False, # Use RP2040 PIO implementation of UART. Required if you want to use other pins than RX/TX
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### `split_side`
|
||||
This tells your microcontroller wich side it handles. It's usually not necessary -- defaulting to `split_side = None` it results in:
|
||||
- Auto dectection of the side from the drive name (ending with 'R'/'L').
|
||||
- The `split_target` will be overriden. Each side will act as a `split_target` if connected to a usb host.
|
||||
|
||||
|
||||
The default will cover most cases, but you can still choose to set all that manually, if for example:
|
||||
- You want to debug and/or upload to both sides at the same time over USB. Explicitly setting `split_side` and `split_target` prevents that both halfs consider themselves as `split_target` when a USB connection is detected.
|
||||
- There are different peripherals on both sides, others than just mirrored the columns (see [`split_flip` section](#split_flip)). That means that the most boards with "flippable" PCBs do **not** need this. The following code is **not** a guideline, but an extraordinary example showing the flexibility of KMK (and would realistically be applicable only in messy handwired keyboards):
|
||||
|
||||
```python
|
||||
from storage import getmount
|
||||
|
||||
side = SplitSide.RIGHT if str(getmount('/').label)[-1] == 'R' else SplitSide.LEFT
|
||||
|
||||
if side == SplitSide.RIGHT:
|
||||
keyboard.col_pins = ...
|
||||
keyboard.row_pins = ...
|
||||
keyboard.diode_orientation = ...
|
||||
else:
|
||||
keyboard.col_pins = ...
|
||||
keyboard.row_pins = ...
|
||||
keyboard.diode_orientation = ...
|
||||
|
||||
split = Split(
|
||||
split_side=side,
|
||||
target_left=True,
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
Note: It is best to stay as consistent as possible, but thanks to the `coord_mapping` feature, none of the `col_pins`, `row_pins` and `diode_orientation` **need** to be the same for both side.
|
||||
It is however necessary for `len(col_pins) * len(row_pins)` to be the same to calculate the offset of the key number on the left side correctly.
|
||||
|
||||
### `split_flip`
|
||||
If your split keyboard uses the **same PCB for both sides**, but vertically flipped, set this to `True`, and `False` otherwise. `True` means the wiring is the same for both side except that the `col_pins` are reversed.
|
||||
|
||||
### `split_target_left`
|
||||
The "split_target" refers to the side that acts as the USB HID.
|
||||
|
||||
Setting `split_side = None` (similar to EE HANDS in QMK) this parameter will be overriden.
|
||||
|
||||
### `uart_flip`
|
||||
If your boards are connected through the **same** pins (like gpio**4** of board A to gpio**4** of board B): use `uart_flip = True`.
|
||||
|
||||
If your boards are connected through **different** pins (like gpio**4** of board A to gpio**10** of board B): use `uart_flip = False`.
|
||||
|
||||
|
||||
### `use_pio`
|
||||
If you're using an RP2040 based board and want the split communication to use other pins than the ones with hardware UART support, you can use the PIO implementation. Typical use cases for this are premade boards, made with QMK's bitbanging protocols in mind.
|
||||
|
||||
In order to enable it, you must:
|
||||
|
||||
- Install Circuitpython version > 7.2,
|
||||
- pass `use_pio=True` into the `Split()` constructor.
|
||||
|
||||
|
||||
### `data_pin`/`data_pin2`
|
||||
For UART `SplitType`: on the `split_target` side, `data_pin` is the one use for RX, `data_pin2` the one for TX.
|
||||
|
||||
## EE HANDS / AUTO HANDEDNESS
|
||||
If you want to plug USB in on either side, or are using Bluetooth, this is for you. For this feature to work your circuitpython drive must be renamed following the guidelines at the beginning of this doc.
|
||||
|
||||
For wired connections you don't need to pass anything. For Bluetooth, remove the `split_side` like this
|
||||
```python
|
||||
# Wired
|
||||
split = Split()
|
||||
# Wireless
|
||||
split = Split(split_type=SplitType.BLE)
|
||||
```
|
22
docs/en/sticky_mod.md
Normal file
22
docs/en/sticky_mod.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Sticky Mod
|
||||
This module allows to hold a modifier while a key is being tapped repeatedly; the modifier will be released when any other key is pressed or released.
|
||||
This is for example useful if you want to switch between open windows with ALT+TAB or CMD+TAB, using only a single key.
|
||||
|
||||
## Enabling the module
|
||||
```python
|
||||
from kmk.modules.sticky_mod import StickyMod
|
||||
sticky_mod = StickyMod()
|
||||
keyboard.modules.append(sticky_mod)
|
||||
keyboard.keymap = [
|
||||
[
|
||||
KC.SM(kc=KC.TAB, mod=KC.LALT),
|
||||
KC.SM(KC.TAB, KC.LSFT(KC.LALT)),
|
||||
],
|
||||
]
|
||||
```
|
||||
|
||||
## Keycodes
|
||||
|
||||
|Key |Description |
|
||||
|-------------------------|-----------------------------------------------|
|
||||
|`KC.SM(KC.key, KC.mod)` |sticky mod |
|
37
docs/en/string_substitution.md
Normal file
37
docs/en/string_substitution.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# String Substitution
|
||||
|
||||
The String Substitution module lets a user replace one typed sequence of characters with another. If a string of characters you type matches an entry in your dictionary, it gets deleted and replaced with the corresponding replacement string.
|
||||
|
||||
Potential uses:
|
||||
|
||||
- Rudimentary auto-correct: replace `yuo` with `you`
|
||||
- Text expansion, à la [espanso](https://github.com/federico-terzi/espanso): when `:sig` is typed, replace it with `John Doe`, or turn `idk` into `I don't know`
|
||||
|
||||
## Usage
|
||||
|
||||
The String Substitution module takes a single argument to be passed during initialization: a user-defined dictionary where the keys are the text to be replaced and the values are the replacement text.
|
||||
|
||||
Example is as follows:
|
||||
|
||||
```python
|
||||
from kmk.modules.string_substitution import StringSubstitution
|
||||
|
||||
my_dictionary = {
|
||||
'yuo': 'you',
|
||||
':sig': 'John Doe',
|
||||
'idk': "I don't know"
|
||||
}
|
||||
string_substitution = StringSubstitution(dictionary=my_dictionary)
|
||||
keyboard.modules.append(string_substitution)
|
||||
```
|
||||
|
||||
### Recommendations
|
||||
|
||||
1. Consider prefixing text expansion entries with a symbol to prevent accidental activations: `:sig`, `!email`, etc.
|
||||
2. If you want multiple similar replacements, consider adding a number to prevent unreachable matches: `replaceme1`, `replaceme2`, etc.
|
||||
|
||||
### Limitations
|
||||
|
||||
1. Currently supports characters for which there is a corresponding keycode in KMK - support for international characters is not implemented.
|
||||
2. Since this runs on your keyboard, it is not context-aware. It can't tell if you are typing in a valid text field or not.
|
||||
3. In the interest of a responsive typing experience, the first valid match will be used as soon as it is found. If your dictionary contains "abc" and "abcd", "abcd" will not be matchable.
|
9
docs/en/support.md
Normal file
9
docs/en/support.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Support
|
||||
|
||||
If you need support with KMK or just want to say hi, find us in [#kmkfw:klar.sh
|
||||
on Matrix](https://matrix.to/#/#kmkfw:klar.sh). This channel is bridged to
|
||||
Discord [here](https://discordapp.com/widget?id=493256121075761173&theme=dark)
|
||||
for convenience.
|
||||
|
||||
If you ask for help in chat or open a bug report, if possible
|
||||
make sure your copy of KMK is up-to-date.
|
62
docs/en/tapdance.md
Normal file
62
docs/en/tapdance.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Tap Dance
|
||||
|
||||
Tap dance is a way to allow a single physical key to work as multiple logical
|
||||
keys / actions without using layers. With basic tap dance, you can trigger these
|
||||
"nested" keys or macros through a series of taps of the physical key within a
|
||||
given timeout.
|
||||
|
||||
The resulting "logical" action works just like any other key - it can be pressed
|
||||
and immediately released, or it can be held. For example, let's take a key
|
||||
`KC.TD(KC.A, KC.B)`. If the tap dance key is tapped and released once quickly,
|
||||
the letter "a" will be sent. If it is tapped and released twice quickly, the
|
||||
letter "b" will be sent. If it is tapped once and held, the letter "a" will be
|
||||
held down until the tap dance key is released. If it is tapped and released once
|
||||
quickly, then tapped and held (both actions within the timeout window), the
|
||||
letter "b" will be held down until the tap dance key is released.
|
||||
|
||||
To use this, you may want to define a `tap_time` value in your keyboard
|
||||
configuration. This is an integer in milliseconds, and defaults to `300`.
|
||||
The timeout is reset after each tap and every tapdance sequence can also define
|
||||
an individual `tap_time`.
|
||||
|
||||
You'll then want to create a sequence of keys using `KC.TD(KC.SOMETHING,
|
||||
KC.SOMETHING_ELSE, MAYBE_THIS_IS_A_MACRO, WHATEVER_YO)`, and place it in your
|
||||
keymap somewhere. The only limits on how many keys can go in the sequence are,
|
||||
theoretically, the amount of RAM your MCU/board has.
|
||||
|
||||
Tap dance supports all `HoldTap` based keys, like mod tap, layer tap, oneshot...
|
||||
it will even honor every option set for those keys.
|
||||
Individual timeouts and prefer hold behavior for every tap in the sequence?
|
||||
Not a problem.
|
||||
|
||||
Here's an example of all this in action:
|
||||
|
||||
```python
|
||||
from kmk.keycodes import KC
|
||||
from kmk.handlers.sequences import send_string
|
||||
from kmk.modules.tapdance import TapDance
|
||||
|
||||
keyboard = KMKKeyboard()
|
||||
|
||||
tapdance = TapDance()
|
||||
tapdance.tap_time = 750
|
||||
keyboard.modules.append(tapdance)
|
||||
|
||||
EXAMPLE_TD = KC.TD(
|
||||
# Tap once for "a"
|
||||
KC.A,
|
||||
# Tap twice for "b", or tap and hold for "left control"
|
||||
KC.MT(KC.B, KC.LCTL, prefer_hold=False),
|
||||
# Tap three times to send a raw string via macro
|
||||
send_string('macros in a tap dance? I think yes'),
|
||||
# Tap four times to toggle layer index 1, tap 3 times and hold for 3s to
|
||||
# momentary toggle layer index 1.
|
||||
KC.TT(1, tap_time=3000),
|
||||
)
|
||||
|
||||
# make the default tap time really short for this tap dance:
|
||||
EXAMPLE_TD2 = KC.TD(KC.A, KC.B, tap_time=80)
|
||||
|
||||
|
||||
keyboard.keymap = [[ ...., EXAMPLE_TD, ....], ....]
|
||||
```
|
Reference in New Issue
Block a user