Compare commits

..

76 Commits

Author SHA1 Message Date
Nick Brassel
a9dcfac7b6 Add flashutils manifest file support. 2025-09-06 20:47:44 +10:00
Nick Brassel
e3534cd705 Merge remote-tracking branch 'upstream/master' into bootstrap 2025-09-06 18:04:02 +10:00
Nick Brassel
ed62464c06 macOS-15 2025-08-11 10:50:50 +10:00
Nick Brassel
3a1f3e440c Parallel builds on Windows GHA. 2025-08-11 10:38:50 +10:00
Nick Brassel
7947cd57a7 Fixup paths. 2025-08-11 10:22:04 +10:00
Nick Brassel
9e0321dfb4 No more tumbleweed. 2025-08-11 09:24:17 +10:00
Nick Brassel
57f61c209c Windows. 2025-08-11 09:22:22 +10:00
Nick Brassel
d5193cf45b Merge remote-tracking branch 'upstream/master' into bootstrap 2025-08-11 08:39:29 +10:00
Nick Brassel
843fdba1d4 OpenSUSE support. 2025-08-07 19:49:18 +10:00
Nick Brassel
b0ca90520e xargs 2025-08-07 19:18:23 +10:00
Nick Brassel
a8cdb0c59b Can we fix it? 2025-08-07 19:08:29 +10:00
Nick Brassel
5e2ea31187 OpenSUSE support. 2025-08-07 18:39:46 +10:00
Nick Brassel
4c09a9fe15 Add CachyOS due to $ID_LIKE not reporting Arch any more. 2025-08-07 14:38:47 +10:00
Nick Brassel
1650dccef0 msys2 system variant tests. 2025-08-07 11:50:11 +10:00
Nick Brassel
c05ffd8777 Oops. 2025-08-07 11:41:03 +10:00
Nick Brassel
4633459329 GH API auth? 2025-08-07 11:35:07 +10:00
Nick Brassel
5de15af41f Windows? 2025-08-07 11:27:42 +10:00
Nick Brassel
00d3627d9d macOS workflow? 2025-08-07 09:08:46 +10:00
Nick Brassel
75115250a6 Fail. 2025-08-07 08:52:18 +10:00
Nick Brassel
cabda8be67 We use which to find a compatible RV32 compiler. 2025-08-07 08:51:38 +10:00
Nick Brassel
d425385664 Print $PATH 2025-08-07 08:36:24 +10:00
Nick Brassel
24492d4058 Dump failures? 2025-08-07 01:31:24 +10:00
Nick Brassel
7af4ead24a Older tar means oblivious to zstd. 2025-08-07 01:22:01 +10:00
Nick Brassel
a369a10ded EPEL? 2025-08-07 01:15:57 +10:00
Nick Brassel
b9991cb6b8 Apparently we need EPEL on RHEL-likes. 2025-08-07 01:11:43 +10:00
Nick Brassel
48846aaa93 qmk setup implies qmk doctor. 2025-08-07 01:04:51 +10:00
Nick Brassel
4d869011af RHEL-like libusb naming woes. 2025-08-07 01:02:03 +10:00
Nick Brassel
a77acc04a0 Workflow? 2025-08-07 00:57:24 +10:00
Nick Brassel
445c12e552 Graceful degradation. 2025-08-06 23:30:56 +10:00
Nick Brassel
0a41dd5b8d Merge remote-tracking branch 'upstream/master' into bootstrap 2025-08-05 12:57:21 +10:00
Nick Brassel
d28377237f Merge remote-tracking branch 'upstream/master' into bootstrap 2025-06-20 21:58:37 +10:00
Nick Brassel
2c8b8ef713 Add CachyOS as apparently some installs don't include $ID_LIKE. 2025-06-19 10:04:12 +10:00
Nick Brassel
44ec55032e sh. 2025-06-04 23:45:24 +10:00
Nick Brassel
d4cb53e264 Merge remote-tracking branch 'upstream/master' into bootstrap 2025-06-04 23:05:30 +10:00
Nick Brassel
70ce814790 Allow for missing udevadm (i.e. GHA) 2025-06-04 23:04:27 +10:00
Nick Brassel
4d7764f7f6 bash-isms not welcome here. 2025-06-04 23:00:31 +10:00
Pascal Getreuer
558cce683f Elaborate error message when a binary is missing. 2025-05-26 08:23:06 +10:00
Nick Brassel
cb46402a5f Apply suggestions from code review
Co-authored-by: Joel Challis <git@zvecr.com>
2025-05-24 09:34:15 +10:00
Nick Brassel
5920274d70 Add warning about distro-provided qmk packages. 2025-05-22 20:30:20 +10:00
Nick Brassel
90ed47945f Add udev rule installation support. 2025-05-22 20:21:43 +10:00
Nick Brassel
6d481f176d Add distribution information to qmk doctor 2025-05-22 19:40:45 +10:00
Nick Brassel
ef67e89836 Oops. 2025-05-13 10:32:14 +10:00
Nick Brassel
92bc5ec044 Merge remote-tracking branch 'upstream/master' into bootstrap 2025-05-13 10:04:14 +10:00
Nick Brassel
c80de06b66 Oops. 2025-05-13 10:02:19 +10:00
Nick Brassel
454767e758 Script args comments. 2025-04-22 22:41:26 +10:00
Nick Brassel
176aa1f7db Docs, first pass. 2025-04-22 22:32:02 +10:00
Nick Brassel
e52491a71f Attempt at installing Windows drivers. 2025-04-22 22:28:34 +10:00
Nick Brassel
758ae5d584 Merge remote-tracking branch 'upstream/master' into bootstrap 2025-04-22 14:22:31 +10:00
Nick Brassel
65c3c0bf6a Merge remote-tracking branch 'upstream/master' into bootstrap 2025-04-22 11:59:17 +10:00
Nick Brassel
77366af7ec uv tools directory move. 2025-04-22 11:35:52 +10:00
Nick Brassel
b998dcfd0e More Windows deps. 2025-04-22 09:43:39 +10:00
Nick Brassel
decf39592e Package naming on QMK MSYS. 2025-04-22 09:25:57 +10:00
Nick Brassel
de8d66b6fd No need for sudo under QMK MSYS 2025-04-22 09:19:07 +10:00
Nick Brassel
a171b36eaf stderr. 2025-04-22 00:49:32 +10:00
Nick Brassel
36c2db8629 Cleanup. 2025-04-22 00:44:10 +10:00
Nick Brassel
c57add534e Apple diff 2025-04-22 00:38:06 +10:00
Nick Brassel
7c7a9e0e34 I suppose we need git too. 2025-04-22 00:28:54 +10:00
Nick Brassel
fa1e51dd19 Python version. 2025-04-21 23:38:33 +10:00
Nick Brassel
0d38d2adcf Fixup chicken-and-egg problem with uv installation. 2025-04-21 23:22:57 +10:00
Nick Brassel
d10d637762 Hard-coded default distrib directory. 2025-04-21 17:26:46 +10:00
Nick Brassel
1fb6323a0d macOS path fix, dos2unix dependency. 2025-04-21 11:32:01 +10:00
Nick Brassel
10faf16edf Potentially uninitialised variable. 2025-04-04 22:01:19 +11:00
Nick Brassel
a8d4e89e41 Allow relocation of uv tools directory, defaulting to inside the MSYS tree on Windows. 2025-04-02 17:08:14 +11:00
Nick Brassel
9b201d362c Spaaaaaaaaace 2025-04-02 16:41:15 +11:00
Nick Brassel
1e80179941 Merge remote-tracking branch 'upstream/master' into bootstrap 2025-04-02 16:41:10 +11:00
Nick Brassel
0d18136fdb Use --confirm or CONFIRM=1 to skip delay, fixup use of sudo when unavailable as root. 2025-03-30 00:28:06 +11:00
Nick Brassel
73b0a6d37c GHA. 2025-03-30 00:24:12 +11:00
Nick Brassel
99bf22a50e Windows doesn't like /dev/tty, pivot to delay with Ctrl-C prompt. 2025-03-28 22:52:52 +11:00
Nick Brassel
1921a4808f Formatting. 2025-03-27 22:39:55 +11:00
Nick Brassel
4ef00cd6e8 Argument parsing. 2025-03-27 22:36:37 +11:00
Nick Brassel
fa170a7c15 Basic package manager stuff on macOS/Linux. 2025-03-27 21:41:02 +11:00
Nick Brassel
6c92b08d8f Update __init__.py 2025-03-21 00:07:35 +11:00
Nick Brassel
ad8c332b59 Distribution path. 2025-03-20 23:53:19 +11:00
Nick Brassel
99f2b767be More essential binaries. 2025-03-19 14:01:02 +11:00
Nick Brassel
ddd0f54c94 Hook up internals of the qmk CLI to use $QMK_DISTRIB_DIR. 2025-03-19 12:15:13 +11:00
Nick Brassel
cb4043ec4c Add environment bootstrap script. 2025-03-19 12:05:06 +11:00
80 changed files with 1175 additions and 1447 deletions

View File

@@ -25,7 +25,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 1
persist-credentials: false

View File

@@ -28,7 +28,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

232
.github/workflows/bootstrap_testing.yml vendored Normal file
View File

@@ -0,0 +1,232 @@
name: Bootstrap Script Testing
on:
push:
branches: [bootstrap]
paths:
- "util/env-bootstrap.sh"
- ".github/workflows/bootstrap_testing.yml"
pull_request:
branches: [master, develop, xap]
paths:
- "util/env-bootstrap.sh"
- ".github/workflows/bootstrap_testing.yml"
workflow_dispatch:
permissions:
contents: read
jobs:
bootstrap-test-linux:
name: Bootstrap (Linux)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
distribution:
# Ubuntu/Debian based
- debian:11
- debian:12
- ubuntu:20.04
- ubuntu:22.04
- ubuntu:24.04
# RHEL/CentOS/Fedora based
- fedora:40
- fedora:41
- fedora:42
- rockylinux:8
- rockylinux:9
- rockylinux/rockylinux:10
- almalinux:8
- almalinux:9
- almalinux:10
# OpenSUSE based (we skip Tumbleweed as it has issues with package versions between pattern installs and other dependencies preinstalled into the base container)
- opensuse/leap:latest
# Arch based
- archlinux:latest
- cachyos/cachyos:latest
- manjarolinux/base:latest
container:
image: ${{ matrix.distribution }}
options: --privileged
steps:
- name: Install base dependencies
run: |
case "${{ matrix.distribution }}" in
*ubuntu*|*debian*)
apt-get update
apt-get install -y sudo git passwd
;;
*fedora*|*rockylinux*|*almalinux*)
dnf install -y sudo git passwd findutils # findutils=xargs
;;
*suse*)
zypper --non-interactive refresh
zypper --non-interactive install sudo git shadow findutils # findutils=xargs
;;
*archlinux*|*cachyos*|*manjaro*)
pacman -Syu --noconfirm
pacman -S --noconfirm sudo git
;;
esac
# Fix PAM configuration for sudo in containers
# Fix /etc/shadow permissions - common issue in container environments
chmod 640 /etc/shadow || chmod 400 /etc/shadow || true
# Disable problematic PAM modules that commonly fail in RHEL-like containers
sed -i 's/^session.*pam_systemd.so/#&/' /etc/pam.d/sudo || true
sed -i 's/^session.*pam_loginuid.so/#&/' /etc/pam.d/sudo || true
# Ensure proper sudoers configuration
echo "Defaults !requiretty" >> /etc/sudoers
echo "Defaults secure_path=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"" >> /etc/sudoers
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: recursive
path: qmk_firmware
- name: Create test user
run: |
# Create a test user for the bootstrap script
useradd -m -s /bin/bash -U testuser
echo 'testuser:testpassword' | chpasswd
# Configure passwordless sudo
echo "root ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers # some distros complain about root not being in sudoers
echo "testuser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
# Test sudo functionality
sudo -u testuser whoami || echo "Sudo test failed, but continuing..."
- name: Move QMK repository to test user home
run: |
# Add upstream remote to the cloned repository so `qmk doctor` doesn't flag a warning
git -C qmk_firmware remote add upstream https://github.com/qmk/qmk_firmware.git
# Move the QMK repository to the test user's home directory
mv qmk_firmware /home/testuser/qmk_firmware
chown -R testuser:testuser /home/testuser/qmk_firmware
- name: Run bootstrap script
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Ensure the bootstrap script can access sudo
sudo -u testuser --preserve-env=GITHUB_TOKEN bash -c "
export CONFIRM=1
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /home/testuser
bash /home/testuser/qmk_firmware/util/env-bootstrap.sh
"
- name: Test QMK CLI
run: |
sudo -u testuser bash -c "
export PATH=/home/testuser/.local/bin:\$PATH
cd /home/testuser
qmk setup -y -H /home/testuser/qmk_firmware # setup implies doctor, no need to run it separately
qmk mass-compile -j $(nproc) -e DUMP_CI_METADATA=yes -f 'keyboard_name==*onekey*' -km reset || touch .failed # Compile a bunch of different platforms
"
cd /home/testuser/qmk_firmware
./util/ci/generate_failure_markdown.sh > $GITHUB_STEP_SUMMARY || true
[ ! -e .failed ] || exit 1
bootstrap-test-macos:
name: Bootstrap (macOS)
strategy:
fail-fast: false
matrix:
os:
- macos-13 # Intel x64
- macos-14 # Apple Silicon ARM64
- macos-15 # Apple Silicon ARM64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: recursive
- name: Run bootstrap script
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Add upstream remote to the cloned repository so `qmk doctor` doesn't flag a warning
git remote add upstream https://github.com/qmk/qmk_firmware.git
# Run the bootstrap script
export CONFIRM=1
sh ./util/env-bootstrap.sh
- name: Test QMK CLI
run: |
# Add QMK CLI to PATH (bootstrap script installs it to ~/.local/bin on macOS)
export PATH="$HOME/.local/bin:$PATH"
qmk setup -y -H . # setup implies doctor, no need to run it separately
qmk mass-compile -j $(sysctl -n hw.ncpu) -e DUMP_CI_METADATA=yes -f 'keyboard_name==*onekey*' -km reset || touch .failed # Compile a bunch of different platforms
./util/ci/generate_failure_markdown.sh > $GITHUB_STEP_SUMMARY || true
[ ! -e .failed ] || exit 1
bootstrap-test-windows:
name: Bootstrap (Windows)
strategy:
fail-fast: false
matrix:
msys-variant:
- mingw64
- clang64
- ucrt64
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
steps:
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msys-variant }}
pacboy: >-
git:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: recursive
- name: Run bootstrap script
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Add upstream remote to the cloned repository so `qmk doctor` doesn't flag a warning
git remote add upstream https://github.com/qmk/qmk_firmware.git
# Run the bootstrap script
export CONFIRM=1
sh ./util/env-bootstrap.sh
- name: Test QMK CLI
run: |
# Add QMK CLI to PATH (bootstrap script installs it to /opt/uv/tools/bin on Windows MSYS2)
export PATH="/opt/uv/tools/bin:$PATH"
qmk setup -y -H . # setup implies doctor, no need to run it separately
qmk mass-compile -j $(nproc) -e DUMP_CI_METADATA=yes -f 'keyboard_name==*onekey*' -km reset || touch .failed # Compile a bunch of different platforms
./util/ci/generate_failure_markdown.sh > $GITHUB_STEP_SUMMARY || true
[ ! -e .failed ] || exit 1

View File

@@ -45,7 +45,7 @@ jobs:
git config --global --add safe.directory '*'
- name: Checkout QMK Firmware
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Determine concurrency
id: generate_slice_length
@@ -83,7 +83,7 @@ jobs:
git config --global --add safe.directory '*'
- name: Checkout QMK Firmware
uses: actions/checkout@v5
uses: actions/checkout@v4
with:
fetch-depth: 0

View File

@@ -37,7 +37,7 @@ jobs:
git config --global --add safe.directory '*'
- name: Checkout QMK Firmware
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Generate build targets
id: generate_targets
@@ -89,7 +89,7 @@ jobs:
git config --global --add safe.directory '*'
- name: Checkout QMK Firmware
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Get target definitions
uses: actions/download-artifact@v5
@@ -136,7 +136,7 @@ jobs:
steps:
- name: Checkout QMK Firmware
uses: actions/checkout@v5
uses: actions/checkout@v4
- name: Download firmwares
uses: actions/download-artifact@v5

View File

@@ -24,7 +24,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
submodules: recursive

View File

@@ -15,7 +15,7 @@ jobs:
if: github.repository == 'qmk/qmk_firmware'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
token: ${{ secrets.QMK_BOT_TOKEN }}
fetch-depth: 0

View File

@@ -30,7 +30,7 @@ jobs:
container: ghcr.io/qmk/qmk_cli
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 1

View File

@@ -21,7 +21,7 @@ jobs:
- riot
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
token: ${{ secrets.QMK_BOT_TOKEN }}
fetch-depth: 0

View File

@@ -26,7 +26,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

View File

@@ -19,7 +19,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

View File

@@ -10,4 +10,4 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@v5

View File

@@ -18,7 +18,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
fetch-depth: 0

View File

@@ -19,7 +19,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Run qmk generators
run: |

View File

@@ -19,7 +19,7 @@ jobs:
- name: Disable safe.directory check
run : git config --global --add safe.directory '*'
- uses: actions/checkout@v5
- uses: actions/checkout@v4
- name: Run qmk generators
run: |

View File

@@ -26,7 +26,7 @@ jobs:
container: ghcr.io/qmk/qmk_cli
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies

View File

@@ -36,7 +36,7 @@ Four times a year QMK runs a process for merging Breaking Changes. A Breaking Ch
## Encoder flip
* Flips the encoder direction so that `clockwise == true` is for actually turning the knob clockwise
* Adds `ENCODER_DIRECTION_FLIP` define, so that reversing the expected direction is simple for users.
* Adds `ENCODER_DIRECTION_FLIP` define, so that reversing the expected dirction is simple for users.
* Cleans up documentation page for encoders

View File

@@ -19,7 +19,7 @@ These PRs move the V-USB driver code out of the qmk_firmware repository and into
Updates all of the per key tap-hold functions to pass the `keyrecord_t` structure, and include documentation changes.
Any remaining versions or code outside of the main repo will need to be converted:
Any remaining versions or code outside of the main repo will need to be converted:
| Old function | New Function |
|------------------------------------------------------|---------------------------------------------------------------------------|
|`uint16_t get_tapping_term(uint16_t keycode)` |`uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record)` |
@@ -38,7 +38,7 @@ After the next breaking change you will not be able to build if `bin/qmk hello`
[#8269](https://github.com/qmk/qmk_firmware/pull/8269)
- Provides debug functionality on ChibiOS/ARM that is more compliant than previous integrations.
- Less maintenance, fewer QMK customisations, and allows QMK to sidestep previous compile and runtime issues.
- Less maintenence, fewer QMK customisations, and allows QMK to sidestep previous compile and runtime issues.
- A `make git-submodule` may be required after pulling the latest QMK Firmware code to update to the new dependency.
### Fixed RGB_DISABLE_AFTER_TIMEOUT to be seconds based & small internals cleanup
@@ -51,10 +51,8 @@ After the next breaking change you will not be able to build if `bin/qmk hello`
The `RGB_DISABLE_AFTER_TIMEOUT` definition is now deprecated, and has been superseded by `RGB_DISABLE_TIMEOUT`. To use the new definition, rename `RGB_DISABLE_AFTER_TIMEOUT` to `RGB_DISABLE_TIMEOUT` in your `config.h` file, and multiply the value set by 1200.
```diff
-#define RGB_DISABLE_AFTER_TIMEOUT 100
+#define RGB_DISABLE_TIMEOUT 120000
```
Before: `#define RGB_DISABLE_AFTER_TIMEOUT 100`
After: `#define RGB_DISABLE_TIMEOUT 120000`
### Switch to qmk forks for everything
@@ -105,8 +103,8 @@ This allows current lily58 firmware to advance with updates to the `split_common
- Alternatively, if you did not change the OLED code from that in `default`, you may find it easier to simply copy the [relevant section](https://github.com/qmk/qmk_firmware/blob/4ac310668501ae6786c711ecc8f01f62ddaa1c0b/keyboards/lily58/keymaps/default/keymap.c#L138-L172). Otherwise, the changes you need to make are as follows (sample change [here](https://github.com/qmk/qmk_firmware/pull/6260/files#diff-20943ea59856e9bdf3d99ecb2eee40b7R138-R173))
- [Remove](https://github.com/qmk/qmk_firmware/pull/6260/files#diff-20943ea59856e9bdf3d99ecb2eee40b7L138-L141) the block
```c
#ifdef SSD1306OLED
iota_gfx_init(!has_usb()); // turns on the display
#ifdef SSD1306OLED
iota_gfx_init(!has_usb()); // turns on the display
#endif
```
- Within the block bounded by `#ifdef OLED_DRIVER_ENABLE` and `#endif // OLED_DRIVER_ENABLE`, add the following block to ensure that your two OLEDs are rotated correctly across the left and right sides:
@@ -129,7 +127,7 @@ oled_rotation_t oled_init_user(oled_rotation_t rotation) {
* Refactor to use split_common and remove split codes under the zinc/revx/
* Add - backlight RGB LED and/or underglow RGB LED option
* Add - continuous RGB animations feature (between L and R halves)
* Add - continuous RGB animations feature (between L and R halves)
* Fix - keymap files to adapt to changes
* all authors of keymaps confirmed this PR
* Update - documents and rules.mk
@@ -166,8 +164,8 @@ void keyboard_pre_init_kb(void) {
- The following changes are for compatibility with the OLED driver. If you don't use the OLED driver you may safely delete [this section](https://github.com/qmk/qmk_firmware/blob/e6b9980bd45c186f7360df68c24b6e05a80c10dc/keyboards/lily58/keymaps/default/keymap.c#L144-L190)
- [Remove](https://github.com/qmk/qmk_firmware/pull/6260/files#diff-20943ea59856e9bdf3d99ecb2eee40b7L91-L158) the block
```c
#ifdef SSD1306OLED
iota_gfx_init(!has_usb()); // turns on the display
#ifdef SSD1306OLED
iota_gfx_init(!has_usb()); // turns on the display
#endif
```
- Within the block bounded by `#ifdef OLED_DRIVER_ENABLE` and `#endif // OLED_DRIVER_ENABLE`, add the following block to ensure that your two OLEDs are rotated correctly across the left and right sides:

View File

@@ -12,7 +12,7 @@ Added support for MK66F18 (Teensy 3.6) microcontroller.
### New command: qmk console ([#12828](https://github.com/qmk/qmk_firmware/pull/12828)) {#new-command-qmk-console}
A new `qmk console` command has been added for attaching to your keyboard's console. It operates similarly to QMK Toolbox by allowing you to connect to one or more keyboard consoles to display debugging messages.
A new `qmk console` command has been added for attaching to your keyboard's console. It operates similiarly to QMK Toolbox by allowing you to connect to one or more keyboard consoles to display debugging messages.
### Improved command: qmk config {#improve-command-qmk-config}
@@ -121,8 +121,8 @@ bool encoder_update_user(uint8_t index, bool clockwise) {
tap_code(KC_UP);
}
}
return true;
// If you return true, this will allow the keyboard level code to run, as well.
return true;
// If you return true, this will allow the keyboard level code to run, as well.
//Returning false will override the keyboard level code. Depending on how the keyboard level function is set up.
}
```

View File

@@ -104,7 +104,7 @@ void dip_switch_update_user(uint8_t index, bool active) {
}
}
void dip_switch_update_mask_kb(uint32_t state) {
void dip_switch_update_mask_kb(uint32_t state) {
dip_switch_update_mask_user(state);
}

View File

@@ -29,7 +29,7 @@ Bootloader configuration is no longer assumed. Keyboards must now set either:
### Rename `AdafruitBLE` to `BluefruitLE` ([#16127](https://github.com/qmk/qmk_firmware/pull/16127))
In preparation of future bluetooth work, the `AdafruitBLE` integration has been renamed to allow potential for any other Adafruit BLE products.
In preparation of future bluetooth work, the `AdafruitBLE` integration has been renamed to allow potential for any other Adafruit BLE products.
### Updated Keyboard Codebases {#updated-keyboard-codebases}

View File

@@ -631,15 +631,14 @@ This command compiles all the External Userspace build targets.
**Usage**:
```
qmk userspace-compile [-h] [-e ENV] [-p] [-n] [-c] [-j PARALLEL] [-t]
qmk userspace-compile [-h] [-e ENV] [-n] [-c] [-j PARALLEL] [-t]
options:
-h, --help show this help message and exit
-e, --env ENV Set a variable to be passed to make. May be passed multiple times.
-p, --print-failures Print failed builds.
-e ENV, --env ENV Set a variable to be passed to make. May be passed multiple times.
-n, --dry-run Don't actually build, just show the commands to be run.
-c, --clean Remove object files before compiling.
-j, --parallel PARALLEL
-j PARALLEL, --parallel PARALLEL
Set the number of parallel make jobs; 0 means unlimited.
-t, --no-temp Remove temporary files during build.
```

View File

@@ -145,7 +145,7 @@ void keyboard_pre_init_user(void) {
This is called when the matrix is initialized, and after some of the hardware has been set up, but before many of the features have been initialized.
This is useful for setting up stuff that you may need elsewhere, but isn't hardware related nor is dependent on where it's started.
This is useful for setting up stuff that you may need elsewhere, but isn't hardware related nor is dependant on where it's started.
### `matrix_init_*` Function Documentation
@@ -209,7 +209,7 @@ You should use this function if you need custom matrix scanning code. It can als
This function gets called at the end of all QMK processing, before starting the next iteration. You can safely assume that QMK has dealt with the last matrix scan at the time that these functions are invoked -- layer states have been updated, USB reports have been sent, LEDs have been updated, and displays have been drawn.
Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To keep your board responsive, it's suggested to do as little as possible during these function calls, potentially throttling their behaviour if you do indeed require implementing something special.
Similar to `matrix_scan_*`, these are called as often as the MCU can handle. To keep your board responsive, it's suggested to do as little as possible during these function calls, potentially throtting their behaviour if you do indeed require implementing something special.
### Example `void housekeeping_task_user(void)` implementation
@@ -246,7 +246,7 @@ void check_rgb_timeout(void) {
}
}
/* Then, call the above functions from QMK's built in post processing functions like so */
/* Runs at the end of each scan loop, check if RGB timeout has occurred or not */
/* Runs at the end of each scan loop, check if RGB timeout has occured or not */
void housekeeping_task_user(void) {
#ifdef RGBLIGHT_TIMEOUT
check_rgb_timeout();
@@ -316,7 +316,7 @@ bool shutdown_kb(bool jump_to_bootloader) {
if (!shutdown_user(jump_to_bootloader)) {
return false;
}
if (jump_to_bootloader) {
// red for bootloader
rgb_matrix_set_color_all(RGB_OFF);

View File

@@ -4,7 +4,7 @@ QMK presents itself to the host as a regular HID keyboard device, and as such re
There are two notable exceptions: the Caterina bootloader, usually seen on Pro Micros, and the HalfKay bootloader shipped with PJRC Teensys, appear as a serial port and a generic HID device respectively, and so do not require a driver.
We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the `qmk_install.sh` script will have already installed the drivers for you.
We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have set up the development environment with MSYS2, the QMK CLI installation script will have already installed the drivers for you.
## Installation

View File

@@ -33,7 +33,7 @@ On ARM platforms the bitbang driver causes connection issues when using it toget
+-------+ +-------+
```
One GPIO pin is needed for the bitbang driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SOFT_SERIAL_PIN` (SSP) in the configuration. A TRS or USB cable provides enough conductors for this driver to function.
One GPIO pin is needed for the bitbang driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SOFT_SERIAL_PIN` (SSP) in the configuration. A TRS or USB cable provides enough conductors for this driver to function.
### Setup
@@ -63,12 +63,12 @@ SERIAL_DRIVER = bitbang
## USART Half-duplex
Targeting ARM boards based on ChibiOS, where communication is offloaded to a USART hardware device that supports Half-duplex operation. The advantages over bitbanging are fast, accurate timings and reduced CPU usage. Therefore it is advised to choose Half-duplex over Bitbang if MCU is capable of utilising Half-duplex, and Full-duplex can't be used instead (e.g. lack of available GPIO pins, or incompatible PCB design).
Targeting ARM boards based on ChibiOS, where communication is offloaded to a USART hardware device that supports Half-duplex operation. The advantages over bitbanging are fast, accurate timings and reduced CPU usage. Therefore it is advised to choose Half-duplex over Bitbang if MCU is capable of utilising Half-duplex, and Full-duplex can't be used instead (e.g. lack of available GPIO pins, or imcompatible PCB design).
### Pin configuration
```
LEFT RIGHT
LEFT RIGHT
+-------+ | | +-------+
| | R R | |
| | | SERIAL | | |
@@ -80,7 +80,7 @@ Targeting ARM boards based on ChibiOS, where communication is offloaded to a USA
+-------+ +-------+
```
Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SERIAL_USART_TX_PIN` in the configuration. Ensure that the pin chosen for split communication can operate as the TX pin of the contoller's USART peripheral. A TRS or USB cable provides enough conductors for this driver to function. As the split connection is configured to operate in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work.
Only one GPIO pin is needed for the Half-duplex driver, as only one wire is used for receiving and transmitting data. This pin is referred to as the `SERIAL_USART_TX_PIN` in the configuration. Ensure that the pin chosen for split communication can operate as the TX pin of the contoller's USART peripheral. A TRS or USB cable provides enough conductors for this driver to function. As the split connection is configured to operate in open-drain mode, an **external pull-up resistor is needed to keep the line high**. Resistor values of 1.5kΩ to 8.2kΩ are known to work.
::: warning
***Note:*** A pull-up resistor isn't required for RP2040 controllers configured with PIO subsystem.
@@ -278,7 +278,7 @@ There are several advanced configuration options that can be defined in your key
### Baudrate
If you're having issues or need a higher baudrate with serial communication, you can change the baudrate which in turn controls the communication speed for serial. You want to lower the baudrate if you experience failed transactions.
If you're having issues or need a higher baudrate with serial communication, you can change the baudrate which in turn controls the communication speed for serial. You want to lower the baudrate if you experience failed transactions.
```c
#define SELECT_SOFT_SERIAL_SPEED n
@@ -307,19 +307,19 @@ This is the default time window in milliseconds in which a successful communicat
## Troubleshooting
If you're having issues with serial communication, you can enable debug messages that will give you insights which part of the communication failed. The enable these messages add to your keyboards `config.h` file:
If you're having issues withe serial communication, you can enable debug messages that will give you insights which part of the communication failed. The enable these messages add to your keyboards `config.h` file:
```c
#define SERIAL_DEBUG
```
::: tip
The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](../faq_debug).
:::
## Alternate Functions for selected STM32 MCUs
Pins for USART Peripherals with
Pins for USART Peripherals with
### STM32F303 / Proton-C [Datasheet](https://www.st.com/resource/en/datasheet/stm32f303cc.pdf)

View File

@@ -44,7 +44,7 @@ Pro Micro (Atmega32u4), make sure to include `CONFIG_USB_ACM=y`. Other devices m
Issues encountered when flashing keyboards on Windows are most often due to having the wrong drivers installed for the bootloader, or none at all.
Re-running the QMK installation script (`./util/qmk_install.sh` from the `qmk_firmware` directory in MSYS2 or WSL) or reinstalling the QMK Toolbox may fix the issue. Alternatively, you can download and run the [`qmk_driver_installer`](https://github.com/qmk/qmk_driver_installer) package manually.
Re-running the QMK installation script (`curl -fsSL https://install.qmk.fm | sh`) or reinstalling the QMK Toolbox may fix the issue. Alternatively, you can download and run the [`qmk_driver_installer`](https://github.com/qmk/qmk_driver_installer) package manually.
If that doesn't work, then you may need to download and run Zadig. See [Bootloader Driver Installation with Zadig](driver_installation_zadig) for more detailed information.

View File

@@ -47,7 +47,7 @@ susceptible to noise, you must choose a debounce method that will also mitigate
* Debounce algorithms often have a 'debounce time' parameter, that specifies the maximum settling time of the switch contacts.
This time might be measured in various units:
* Cycles-based debouncing waits n cycles (scans), decreasing count by one each matrix_scan
* Timestamp-based debouncing stores the millisecond timestamp a change occurred, and does subtraction to figure out time elapsed.
* Timestamp-based debouncing stores the millisecond timestamp a change occurred, and does substraction to figure out time elapsed.
* Timestamp-based debouncing is usually superior, especially in the case of noise-resistant devices because settling times of physical
switches is specified in units of time, and should not depend on the matrix scan-rate of the keyboard.
* Cycles-based debouncing is sometimes considered inferior, because the settling time that it is able to compensate for depends on the

View File

@@ -100,7 +100,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) { //This disables layer indication, as it's assumed that if you're changing this ... you want that disabled
if (user_config.rgb_layer_change) { // only if this is enabled
user_config.rgb_layer_change = false; // disable it, and
eeconfig_update_user(user_config.raw); // write the settings to EEPROM
eeconfig_update_user(user_config.raw); // write the setings to EEPROM
}
}
return true; break;
@@ -109,7 +109,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
}
```
And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EE_CLR` keycode or [Bootmagic](features/bootmagic) functionality. For example, if you want to set rgb layer indication by default, and save the default valued.
And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EE_CLR` keycode or [Bootmagic](features/bootmagic) functionallity. For example, if you want to set rgb layer indication by default, and save the default valued.
```c
void eeconfig_init_user(void) { // EEPROM is getting reset!

View File

@@ -25,7 +25,7 @@ active layer until pressed again.
Currently, the `layer` argument of `LT()` is limited to layers 0-15, and the `kc` argument to the [Basic Keycode set](keycodes_basic), meaning you can't use keycodes like `LCTL()`, `KC_TILD`, or anything greater than `0xFF`. This is because QMK uses 16-bit keycodes, of which 4 bits are used for the function identifier and 4 bits for the layer, leaving only 8 bits for the keycode.
For a similar reason, the `layer` argument of `LM()` is also limited to layers 0-15 and the `mod` argument must fit within 5 bits. As a consequence, although left and right modifiers are supported by `LM()`, it is impossible to mix and match left and right modifiers. Specifying at least one right-hand modifier in a combination such as `MOD_RALT|MOD_LSFT` will convert *all* the listed modifiers to their right-hand counterpart. So, using the aforementioned mod-mask will actually send <kbd>Right Alt</kbd>+<kbd>Right Shift</kbd>. Make sure to use the `MOD_xxx` constants over alternative ways of specifying modifiers when defining your layer-mod key.
For a similar reason, the `layer` argument of `LM()` is also limited to layers 0-15 and the `mod` argument must fit within 5 bits. As a consequence, although left and right modifiers are supported by `LM()`, it is impossible to mix and match left and right modifiers. Specifying at least one right-hand modifier in a combination such as `MOD_RALT|MOD_LSFT` will convert *all* the listed modifiers to their right-hand counterpart. So, using the aforementionned mod-mask will actually send <kbd>Right Alt</kbd>+<kbd>Right Shift</kbd>. Make sure to use the `MOD_xxx` constants over alternative ways of specifying modifiers when defining your layer-mod key.
| `LM(1,KC_LSFT)` | `LM(1,MOD_MASK_SHIFT)` | `LM(1,MOD_BIT(KC_LSFT))` | `LM(1,MOD_LSFT)` |
|:---------------:|:----------------------:|:------------------------:|:----------------:|
@@ -61,27 +61,27 @@ Sometimes, you might want to switch between layers in a macro or as part of a ta
There are a number of functions (and variables) related to how you can use or manipulate the layers.
|Function |Description |
|---------------------------------------------|--------------------------------------------------------------------------------------------------------|
|`layer_state_set(layer_mask)` |Directly sets the layer state (avoid unless you know what you are doing). |
|`layer_clear()` |Clears all layers (turns them all off). |
|`layer_move(layer)` |Turns specified layer on, and all other layers off. |
|`layer_on(layer)` |Turns specified layer on, leaves all other layers in existing state. |
|`layer_off(layer)` |Turns specified layer off, leaves all other layers in existing state. |
|`layer_invert(layer)` |Inverts/toggles the state of the specified layer |
|`layer_or(layer_mask)` |Turns on layers based on matching bits between specified layer and existing layer state. |
|`layer_and(layer_mask)` |Turns on layers based on matching enabled bits between specified layer and existing layer state. |
|`layer_xor(layer_mask)` |Turns on layers based on non-matching bits between specified layer and existing layer state. |
|`layer_debug(layer_mask)` |Prints out the current bit mask and highest active layer to debugger console. |
|`default_layer_set(layer_mask)` |Directly sets the default layer state (avoid unless you know what you are doing). |
|`default_layer_or(layer_mask)` |Turns on layers based on matching bits between specified layer and existing default layer state. |
|`default_layer_and(layer_mask)` |Turns on layers based on matching enabled bits between specified layer and existing default layer state.|
|`default_layer_xor(layer_mask)` |Turns on layers based on non-matching bits between specified layer and existing default layer state. |
|`default_layer_debug(layer_mask)` |Prints out the current bit mask and highest active default layer to debugger console. |
|[`set_single_default_layer(layer)`](ref_functions.md#setting-the-persistent-default-layer) |Sets the default layer, but does _not_ write it to persistent memory (EEPROM). |
|[`set_single_persistent_default_layer(layer)`](ref_functions.md#setting-the-persistent-default-layer)|Sets the default layer and writes it to persistent memory (EEPROM). |
|[`update_tri_layer(x, y, z)`](ref_functions.md#update_tri_layerx-y-z) |Checks if layers `x` and `y` are both on, and sets `z` based on that (on if both on, otherwise off).|
|[`update_tri_layer_state(state, x, y, z)`](ref_functions.md#update_tri_layer_statestate-x-y-z) |Does the same as `update_tri_layer(x, y, z)`, but from `layer_state_set_*` functions. |
|Function |Description |
|----------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `layer_state_set(layer_mask)` | Directly sets the layer state (avoid unless you know what you are doing). |
| `layer_clear()` | Clears all layers (turns them all off). |
| `layer_move(layer)` | Turns specified layer on, and all other layers off. |
| `layer_on(layer)` | Turns specified layer on, leaves all other layers in existing state. |
| `layer_off(layer)` | Turns specified layer off, leaves all other layers in existing state. |
| `layer_invert(layer)` | Inverts/toggles the state of the specified layer |
| `layer_or(layer_mask)` | Turns on layers based on matching bits between specifed layer and existing layer state. |
| `layer_and(layer_mask)` | Turns on layers based on matching enabled bits between specifed layer and existing layer state. |
| `layer_xor(layer_mask)` | Turns on layers based on non-matching bits between specifed layer and existing layer state. |
| `layer_debug(layer_mask)` | Prints out the current bit mask and highest active layer to debugger console. |
| `default_layer_set(layer_mask)` | Directly sets the default layer state (avoid unless you know what you are doing). |
| `default_layer_or(layer_mask)` | Turns on layers based on matching bits between specifed layer and existing default layer state. |
| `default_layer_and(layer_mask)` | Turns on layers based on matching enabled bits between specifed layer and existing default layer state. |
| `default_layer_xor(layer_mask)` | Turns on layers based on non-matching bits between specifed layer and existing default layer state. |
| `default_layer_debug(layer_mask)` | Prints out the current bit mask and highest active default layer to debugger console. |
| [`set_single_default_layer(layer)`](ref_functions.md#setting-the-persistent-default-layer) | Sets the default layer, but does _not_ write it to persistent memory (EEPROM). |
| [`set_single_persistent_default_layer(layer)`](ref_functions.md#setting-the-persistent-default-layer) | Sets the default layer and writes it to persistent memory (EEPROM). |
| [`update_tri_layer(x, y, z)`](ref_functions.md#update_tri_layerx-y-z) | Checks if layers `x` and `y` are both on, and sets `z` based on that (on if both on, otherwise off). |
| [`update_tri_layer_state(state, x, y, z)`](ref_functions.md#update_tri_layer_statestate-x-y-z) | Does the same as `update_tri_layer(x, y, z)`, but from `layer_state_set_*` functions. |
In addition to the functions that you can call, there are a number of callback functions that get called every time the layer changes. This passes the layer state to the function, where it can be read or modified.
@@ -154,7 +154,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KC_CYCLE_LAYERS:
// Our logic will happen on presses, nothing is done on releases
if (!record->event.pressed) {
if (!record->event.pressed) {
// We've already handled the keycode (doing nothing), let QMK know so no further code is run unnecessarily
return false;
}

View File

@@ -24,10 +24,10 @@ For example,
make planck:jack
Will include the `/users/jack/` folder in the path, along with `/users/jack/rules.mk`.
Will include the `/users/jack/` folder in the path, along with `/users/jack/rules.mk`.
::: warning
This `name` can be [overridden](#override-default-userspace), if needed.
This `name` can be [overridden](#override-default-userspace), if needed.
:::
## `Rules.mk`
@@ -38,9 +38,9 @@ It's highly recommended that you use `<name>.c` as the default source file to be
SRC += <name>.c
Additional files may be added in the same way - it's recommended you have one named `<name>`.c/.h to start off with, though.
Additional files may be added in the same way - it's recommended you have one named `<name>`.c/.h to start off with, though.
The `/users/<name>/rules.mk` file will be included in the build _after_ the `rules.mk` from your keymap. This allows you to have features in your userspace `rules.mk` that depend on individual QMK features that may or may not be available on a specific keyboard.
The `/users/<name>/rules.mk` file will be included in the build _after_ the `rules.mk` from your keymap. This allows you to have features in your userspace `rules.mk` that depend on individual QMK features that may or may not be available on a specific keyboard.
For example, if you have RGB control features shared between all your keyboards that support RGB lighting, you can add support for that if the RGBLIGHT feature is enabled:
```make
@@ -82,7 +82,7 @@ You should use the `config.h` for [configuration options](config_options), and t
Please include authorship (your name, GitHub username, email), and optionally [a license that's GPL compatible](https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses).
You can use this as a template:
You can use this as a template:
```
Copyright <year> <name> <email> @<github_username>
@@ -100,9 +100,9 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```
You'd want to replace the year, name, email and GitHub username with your info.
You'd want to replace the year, name, email and GitHub username with your info.
Additionally, this is a good place to document your code, if you wish to share it with others.
Additionally, this is a good place to document your code, if you wish to share it with others.
## Build All Keyboards That Support a Specific Keymap
@@ -118,21 +118,20 @@ This is ideal for when you want ensure everything compiles successfully when pre
## Examples
For a brief example, checkout [`/users/_example/`](https://github.com/qmk/qmk_firmware/tree/master/users/_example).
For more complicated examples, checkout the [`awesome-qmk` collection](https://github.com/qmk/awesome-qmk).
For a brief example, checkout [`/users/_example/`](https://github.com/qmk/qmk_firmware/tree/master/users/_example).
For more complicated examples, checkout the [`awesome-qmk` colletion](https://github.com/qmk/awesome-qmk).
### Customized Functions
QMK has a bunch of [functions](custom_quantum_functions) that have [`_quantum`, `_kb`, and `_user` versions](custom_quantum_functions#a-word-on-core-vs-keyboards-vs-keymap) that you can use. You will pretty much always want to use the user version of these functions. But the problem is that if you use them in your userspace, then you don't have a version that you can use in your keymap.
QMK has a bunch of [functions](custom_quantum_functions) that have [`_quantum`, `_kb`, and `_user` versions](custom_quantum_functions#a-word-on-core-vs-keyboards-vs-keymap) that you can use. You will pretty much always want to use the user version of these functions. But the problem is that if you use them in your userspace, then you don't have a version that you can use in your keymap.
However, you can actually add support for keymap version, so that you can use it in both your userspace and your keymap!
However, you can actually add support for keymap version, so that you can use it in both your userspace and your keymap!
For instance, let's look at the `layer_state_set_user()` function. You can enable the [Tri Layer State](ref_functions#olkb-tri-layers) functionality on all of your boards, while also retaining the Tri Layer functionality in your `keymap.c` files.
For instance, let's look at the `layer_state_set_user()` function. You can enable the [Tri Layer State](ref_functions#olkb-tri-layers) functionality on all of your boards, while also retaining the Tri Layer functionality in your `keymap.c` files.
In your `<name.c>` file, you'd want to add this:
In your `<name.c>` file, you'd want to add this:
```c
__attribute__ ((weak))
layer_state_t layer_state_set_keymap (layer_state_t state) {
@@ -144,7 +143,7 @@ layer_state_t layer_state_set_user (layer_state_t state) {
return layer_state_set_keymap (state);
}
```
The `__attribute__ ((weak))` part tells the compiler that this is a placeholder function that can then be replaced by a version in your `keymap.c`. That way, you don't need to add it to your `keymap.c`, but if you do, you won't get any conflicts because the function is the same name.
The `__attribute__ ((weak))` part tells the compiler that this is a placeholder function that can then be replaced by a version in your `keymap.c`. That way, you don't need to add it to your `keymap.c`, but if you do, you won't get any conflicts because the function is the same name.
The `_keymap` part here doesn't matter, it just needs to be something other than `_quantum`, `_kb`, or `_user`, since those are already in use. So you could use `layer_state_set_mine`, `layer_state_set_fn`, or anything else.
@@ -152,7 +151,7 @@ You can see a list of this and other common functions in [`template.c`](https://
### Custom Features
Since the Userspace feature can support a staggering number of boards, you may have boards that you want to enable certain functionality for, but not for others. And you can actually create "features" that you can enable or disable in your own userspace.
Since the Userspace feature can support a staggering number of boards, you may have boards that you want to enable certain functionality for, but not for others. And you can actually create "features" that you can enable or disable in your own userspace.
For instance, if you wanted to have a bunch of macros available, but only on certain boards (to save space), you could "hide" them being a `#ifdef MACROS_ENABLED`, and then enable it per board. To do this, add this to your rules.mk
```make
@@ -160,11 +159,11 @@ ifeq ($(strip $(MACROS_ENABLED)), yes)
OPT_DEFS += -DMACROS_ENABLED
endif
```
The `OPT_DEFS` setting causes `MACROS_ENABLED` to be defined for your keyboards (note the `-D` in front of the name), and you could use `#ifdef MACROS_ENABLED` to check the status in your c/h files, and handle that code based on that.
The `OPT_DEFS` setting causes `MACROS_ENABLED` to be defined for your keyboards (note the `-D` in front of the name), and you could use `#ifdef MACROS_ENABLED` to check the status in your c/h files, and handle that code based on that.
Then you add `MACROS_ENABLED = yes` to the `rules.mk` for you keymap to enable this feature and the code in your userspace.
And in your `process_record_user` function, you'd do something like this:
And in your `process_record_user` function, you'd do something like this:
```c
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
@@ -188,9 +187,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
### Consolidated Macros
If you wanted to consolidate macros and other functions into your userspace for all of your keymaps, you can do that. This builds upon the [Customized Functions](#customized-functions) example above. This lets you maintain a bunch of macros that are shared between the different keyboards, and allow for keyboard specific macros, too.
If you wanted to consolidate macros and other functions into your userspace for all of your keymaps, you can do that. This builds upon the [Customized Functions](#customized-functions) example above. This lets you maintain a bunch of macros that are shared between the different keyboards, and allow for keyboard specific macros, too.
First, you'd want to go through all of your `keymap.c` files and replace `process_record_user` with `process_record_keymap` instead. This way, you can still use keyboard specific codes on those boards, and use your custom "global" keycodes as well. You'll also want to replace `SAFE_RANGE` with `NEW_SAFE_RANGE` so that you won't have any overlapping keycodes
First, you'd want to go through all of your `keymap.c` files and replace `process_record_user` with `process_record_keymap` instead. This way, you can still use keyboard specific codes on those boards, and use your custom "global" keycodes as well. You'll also want to replace `SAFE_RANGE` with `NEW_SAFE_RANGE` so that you wont have any overlapping keycodes
Then add `#include "<name>.h"` to all of your keymap.c files. This allows you to use these new keycodes without having to redefine them in each keymap.
@@ -246,7 +245,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
```
For boards that may not have a shift button (such as on a macro pad), we need a way to always include the bootloader option. To do that, add the following to the `rules.mk` in your userspace folder:
For boards that may not have a shift button (such as on a macro pad), we need a way to always include the bootloader option. To do that, add the following to the `rules.mk` in your userspace folder:
```make
ifeq ($(strip $(FLASH_BOOTLOADER)), yes)
@@ -256,7 +255,7 @@ endif
This will add a new `KC_MAKE` keycode that can be used in any of your keymaps. And this keycode will output `make <keyboard>:<keymap>`, making frequent compiling easier. And this will work with any keyboard and any keymap as it will output the current boards info, so that you don't have to type this out every time.
Also, holding Shift will add the flash target (`:flash`) to the command. Holding Control will add some commands that will speed up compiling time by processing multiple files at once.
Also, holding Shift will add the flash target (`:flash`) to the command. Holding Control will add some commands that will speed up compiling time by processing multiple files at once.
And for the boards that lack a shift key, or that you want to always attempt the flashing part, you can add `FLASH_BOOTLOADER = yes` to the `rules.mk` of that keymap.

View File

@@ -73,13 +73,16 @@ Should you rather choose to generate and use your own sample-table with the DAC
### PWM (software)
If the DAC pins are unavailable (or the MCU has no usable DAC at all, like STM32F1xx); PWM can be an alternative.
if the DAC pins are unavailable (or the MCU has no usable DAC at all, like STM32F1xx); PWM can be an alternative.
Note that there is currently only one speaker/pin supported.
To use this feature, set `AUDIO_DRIVER = pwm_software` in `rules.mk` and set `#define AUDIO_PIN C13` (can be any pin) in `config.h` to have the selected pin output a pwm signal, generated from a timer callback which toggles the pin in software.
set in `rules.mk`:
`AUDIO_DRIVER = pwm_software` and in `config.h`:
`#define AUDIO_PIN C13` (can be any pin) to have the selected pin output a pwm signal, generated from a timer callback which toggles the pin in software.
#### Wiring
The usual piezo wiring: red goes to the selected AUDIO_PIN, black goes to ground.
the usual piezo wiring: red goes to the selected AUDIO_PIN, black goes to ground.
OR if you can chose to drive one piezo with two pins, for example `#define AUDIO_PIN B1`, `#define AUDIO_PIN_ALT B2` in `config.h`, with `#define AUDIO_PIN_ALT_AS_NEGATIVE` - then the red lead could go to B1, the black to B2.
@@ -156,7 +159,7 @@ PLAY_LOOP(my_song);
It's advised that you wrap all audio features in `#ifdef AUDIO_ENABLE` / `#endif` to avoid causing problems when audio isn't built into the keyboard.
The available keycodes for audio are:
The available keycodes for audio are:
|Key |Aliases |Description |
|-------------------------|---------|-------------------------------------------|
@@ -175,8 +178,8 @@ These keycodes turn all of the audio functionality on and off. Turning it off m
|`AUDIO_PIN` | *Not defined* |Configures the pin that the speaker is connected to. |
|`AUDIO_PIN_ALT` | *Not defined* |Configures the pin for a second speaker or second pin connected to one speaker. |
|`AUDIO_PIN_ALT_AS_NEGATIVE` | *Not defined* |Enables support for one speaker connected to two pins. |
|`AUDIO_INIT_DELAY` | *Not defined* |Enables delay during startup song to accommodate for USB startup issues. |
|`AUDIO_ENABLE_TONE_MULTIPLEXING` | *Not defined* |Enables time splicing/multiplexing to create multiple tones simultaneously. |
|`AUDIO_INIT_DELAY` | *Not defined* |Enables delay during startup song to accomidate for USB startup issues. |
|`AUDIO_ENABLE_TONE_MULTIPLEXING` | *Not defined* |Enables time splicing/multiplexing to create multiple tones simutaneously. |
|`AUDIO_POWER_CONTROL_PIN` | *Not defined* |Enables power control code to enable or cut off power to speaker (such as with PAM8302 amp). |
|`AUDIO_POWER_CONTROL_PIN_ON_STATE`| `1` |The state of the audio power control pin when audio is "on" - `1` for high, `0` for low. |
|`STARTUP_SONG` | `STARTUP_SOUND` |Plays when the keyboard starts up (audio.c) |
@@ -298,9 +301,9 @@ Things that return false are not part of the mask, and are always processed.
### Music Map
By default, the Music Mode uses the columns and row to determine the scale for the keys. For a board that uses a rectangular matrix that matches the keyboard layout, this is just fine. However, for boards that use a more complicated matrix (such as the Planck Rev6, or many split keyboards) this would result in a very skewed experience.
By default, the Music Mode uses the columns and row to determine the scale for the keys. For a board that uses a rectangular matrix that matches the keyboard layout, this is just fine. However, for boards that use a more complicated matrix (such as the Planck Rev6, or many split keyboards) this would result in a very skewed experience.
However, the Music Map option allows you to remap the scaling for the music mode, so it fits the layout, and is more natural.
However, the Music Map option allows you to remap the scaling for the music mode, so it fits the layout, and is more natural.
To enable this feature, add `#define MUSIC_MAP` to your `config.h` file, and then you will want to add a `uint8_t music_map` to your keyboard's `c` file, or your `keymap.c`.
@@ -313,13 +316,13 @@ const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS] = LAYOUT_ortho_4x12(
);
```
You will want to use whichever `LAYOUT` macro that your keyboard uses here. This maps it to the correct key location. Start in the bottom left of the keyboard layout, and move to the right, and then upwards. Fill in all the entries until you have a complete matrix.
You will want to use whichever `LAYOUT` macro that your keyboard uses here. This maps it to the correct key location. Start in the bottom left of the keyboard layout, and move to the right, and then upwards. Fill in all the entries until you have a complete matrix.
You can look at the [Planck Keyboard](https://github.com/qmk/qmk_firmware/blob/e9ace1487887c1f8b4a7e8e6d87c322988bec9ce/keyboards/planck/planck.c#L24-L29) as an example of how to implement this.
You can look at the [Planck Keyboard](https://github.com/qmk/qmk_firmware/blob/e9ace1487887c1f8b4a7e8e6d87c322988bec9ce/keyboards/planck/planck.c#L24-L29) as an example of how to implement this.
## Audio Click
This adds a click sound each time you hit a button, to simulate click sounds from the keyboard. And the sounds are slightly different for each keypress, so it doesn't sound like a single long note, if you type rapidly.
This adds a click sound each time you hit a button, to simulate click sounds from the keyboard. And the sounds are slightly different for each keypress, so it doesn't sound like a single long note, if you type rapidly.
Keycodes available:
@@ -338,7 +341,7 @@ The feature is disabled by default, to save space. To enable it, add this to yo
#define AUDIO_CLICKY
```
You can configure the default, min and max frequencies, the stepping and built in randomness by defining these values:
You can configure the default, min and max frequencies, the stepping and built in randomness by defining these values:
| Option | Default Value | Description |
|--------|---------------|-------------|
@@ -346,7 +349,7 @@ You can configure the default, min and max frequencies, the stepping and built i
| `AUDIO_CLICKY_FREQ_MIN` | 65.0f | Sets the lowest frequency (under 60f are a bit buggy). |
| `AUDIO_CLICKY_FREQ_MAX` | 1500.0f | Sets the highest frequency. Too high may result in coworkers attacking you. |
| `AUDIO_CLICKY_FREQ_FACTOR` | 1.18921f| Sets the stepping of UP/DOWN key codes. This is a multiplicative factor. The default steps the frequency up/down by a musical minor third. |
| `AUDIO_CLICKY_FREQ_RANDOMNESS` | 0.05f | Sets a factor of randomness for the clicks, Setting this to `0f` will make each click identical, and `1.0f` will make this sound much like the 90's computer screen scrolling/typing effect. |
| `AUDIO_CLICKY_FREQ_RANDOMNESS` | 0.05f | Sets a factor of randomness for the clicks, Setting this to `0f` will make each click identical, and `1.0f` will make this sound much like the 90's computer screen scrolling/typing effect. |
| `AUDIO_CLICKY_DELAY_DURATION` | 1 | An integer note duration where 1 is 1/16th of the tempo, or a sixty-fourth note (see `quantum/audio/musical_notes.h` for implementation details). The main clicky effect will be delayed by this duration. Adjusting this to values around 6-12 will help compensate for loud switches. |
## MIDI Functionality

View File

@@ -46,7 +46,7 @@ When [handedness](split_keyboard#setting-handedness) is predetermined via option
}
```
If you pick the top right key for the right half, it is `R05` on the top layout. Within the key matrix below, `R05` is located on row 4 column 4. To use that key as the right half's Bootmagic trigger, add these entries to your `config.h` file:
If you pick the top right key for the right half, it is `R05` on the top layout. Within the key matrix below, `R05` is located on row 4 columnn 4. To use that key as the right half's Bootmagic trigger, add these entries to your `config.h` file:
```c
#define BOOTMAGIC_ROW_RIGHT 4

View File

@@ -68,7 +68,7 @@ Additionally, if one side does not have an encoder, you can specify `{}` for the
```
::: warning
Keep in mind that whenever you change the encoder resolution, you will need to reflash the half that has the encoder affected by the change.
Keep in mind that whenver you change the encoder resolution, you will need to reflash the half that has the encoder affected by the change.
:::
## Encoder map {#encoder-map}

View File

@@ -44,14 +44,14 @@ Not all keycodes below will work depending on which haptic mechanism you have ch
|`QK_HAPTIC_MODE_NEXT` |`HF_NEXT`| Go to next DRV2605L waveform |
|`QK_HAPTIC_MODE_PREVIOUS` |`HF_PREV`| Go to previous DRV2605L waveform |
|`QK_HAPTIC_CONTINUOUS_TOGGLE`|`HF_CONT`| Toggle continuous haptic mode on/off |
|`QK_HAPTIC_CONTINUOUS_UP` |`HF_CONU`| Increase DRV2605L continuous haptic strength |
|`QK_HAPTIC_CONTINUOUS_DOWN` |`HF_COND`| Decrease DRV2605L continuous haptic strength |
|`QK_HAPTIC_CONTINUOUS_UP` |`HF_CONU`| Increase DRV2605L continous haptic strength |
|`QK_HAPTIC_CONTINUOUS_DOWN` |`HF_COND`| Decrease DRV2605L continous haptic strength |
|`QK_HAPTIC_DWELL_UP` |`HF_DWLU`| Increase Solenoid dwell time |
|`QK_HAPTIC_DWELL_DOWN` |`HF_DWLD`| Decrease Solenoid dwell time |
### Solenoids
The solenoid code supports relay switches, and similar hardware, as well as solenoids.
The solenoid code supports relay switches, and similar hardware, as well as solenoids.
For a regular solenoid, you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid.
@@ -75,7 +75,7 @@ For relay switches, the hardware may already contain all of that ciruitry, and j
|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the switch is in buzz mode. |
* If solenoid buzz is off, then dwell time is how long the "plunger" stays activated. The dwell time changes how the solenoid sounds.
* If solenoid buzz is on, then dwell time sets the length of the buzz, while `SOLENOID_BUZZ_ACTUATED` and `SOLENOID_BUZZ_NONACTUATED` set the (non-)actuation times within the buzz period.
* If solenoid buzz is on, then dwell time sets the length of the buzz, while `SOLENOID_BUZZ_ACTUATED` and `SOLENOID_BUZZ_NONACTUATED` set the (non-)actuation times withing the buzz period.
* With the current implementation, for any of the above time settings, the precision of these settings may be affected by how fast the keyboard is able to scan the matrix.
Therefore, if the keyboards scanning routine is slow, it may be preferable to set `SOLENOID_DWELL_STEP_SIZE` to a value slightly smaller than the time it takes to scan the keyboard.
@@ -104,7 +104,7 @@ Eccentric Rotating Mass vibration motors (ERM) is motor with a off-set weight at
```
##### LRA
Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommended range for this frequency.
Linear resonant actuators (LRA, also know as a linear vibrator) works different from a ERM. A LRA has a weight and magnet suspended by springs and a voice coil. When the drive signal is applied, the weight would be vibrate on a single axis (side to side or up and down). Since the weight is attached to a spring, there is a resonance effect at a specific frequency. This frequency is where the LRA will operate the most efficiently. Refer to the motor's datasheet for the recommanded range for this frequency.
```c
#define DRV2605L_FB_ERM_LRA 1
@@ -114,7 +114,7 @@ Linear resonant actuators (LRA, also know as a linear vibrator) works different
/* Please refer to your datasheet for the optimal setting for your specific motor. */
#define DRV2605L_RATED_VOLTAGE 2
#define DRV2605L_V_PEAK 2.8
#define DRV2605L_V_RMS 2.0
#define DRV2605L_V_RMS 2.0
#define DRV2605L_V_PEAK 2.1
#define DRV2605L_F_LRA 205 /* resonance freq */
```
@@ -125,7 +125,7 @@ DRV2605L comes with preloaded library of various waveform sequences that can be
List of waveform sequences from the datasheet:
|seq# | Sequence name |seq# | Sequence name |seq# |Sequence name |
|seq# | Sequence name |seq# | Sequence name |seq# |Sequence name |
|-----|---------------------|-----|-----------------------------------|-----|--------------------------------------|
| 1 | strong_click | 43 | lg_dblclick_med_60 | 85 | transition_rampup_med_smooth2 |
| 2 | strong_click_60 | 44 | lg_dblsharp_tick | 86 | transition_rampup_short_smooth1 |

View File

@@ -51,7 +51,7 @@ The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surfac
| `ADNS9800_CS_PIN` | (Required) Sets the Chip Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI.
The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI.
### Analog Joystick
@@ -258,7 +258,7 @@ To use the paw 3204 sensor, add this to your `rules.mk`
POINTING_DEVICE_DRIVER = paw3204
```
The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source.
The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source.
| Setting (`config.h`) | Description | Default |
| -------------------- |--------------------------------------------------------------- | -------------------------- |
@@ -275,7 +275,7 @@ To use the Pimoroni Trackball module, add this to your `rules.mk`:
POINTING_DEVICE_DRIVER = pimoroni_trackball
```
The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball.
The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball.
| Setting (`config.h`) | Description | Default |
| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- |
@@ -386,7 +386,7 @@ void pointing_device_driver_set_cpi(uint16_t cpi) {}
```
::: warning
Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/pointing_device_drivers.c`, but there may be cases where it's very specific to the hardware. So these functions are provided, just in case.
Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/pointing_device_drivers.c`, but there may be cases where it's very specific to the hardware. So these functions are provided, just in case.
:::
## Common Configuration
@@ -413,7 +413,7 @@ Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/
When using `SPLIT_POINTING_ENABLE` the `POINTING_DEVICE_MOTION_PIN` functionality is not supported and `POINTING_DEVICE_TASK_THROTTLE_MS` will default to `1`. Increasing this value will increase transport performance at the cost of possible mouse responsiveness.
:::
The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_SCLK_PIN` provide a convenient way to define a single pin that can be used for an interchangeable sensor config. This allows you to have a single config, without defining each device. Each sensor allows for this to be overridden with their own defines.
The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_SCLK_PIN` provide a convenient way to define a single pin that can be used for an interchangeable sensor config. This allows you to have a single config, without defining each device. Each sensor allows for this to be overridden with their own defines.
::: warning
Any pointing device with a lift/contact status can integrate inertial cursor feature into its driver, controlled by `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `get_report()` is needed to generate glide reports.
@@ -424,7 +424,7 @@ Any pointing device with a lift/contact status can integrate inertial cursor fea
| Setting | Description | Default |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `POINTING_DEVICE_HIRES_SCROLL_ENABLE` | (Optional) Enables high resolution scrolling. | _not defined_ |
| `POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER`| (Optional) Resolution multiplier value used by high resolution scrolling. Must be between 1 and 127, inclusive. | `120` |
| `POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER`| (Optional) Resolution mutiplier value used by high resolution scrolling. Must be between 1 and 127, inclusive. | `120` |
| `POINTING_DEVICE_HIRES_SCROLL_EXPONENT` | (Optional) Resolution exponent value used by high resolution scrolling. Must be between 0 and 127, inclusive. | `0` |
The `POINTING_DEVICE_HIRES_SCROLL_ENABLE` setting enables smooth and continuous scrolling when using trackballs or high-end encoders as mouse wheels (as opposed to the typical stepped behavior of most mouse wheels).
@@ -435,7 +435,7 @@ If even smoother scrolling than provided by this default value is desired, first
The function `pointing_device_get_hires_scroll_resolution()` can be called to get the pre-computed resolution multiplier value as a `uint16_t`.
::: warning
High resolution scrolling usually results in larger and/or more frequent mouse reports. This can result in overflow errors and overloading of the host computer's input buffer.
High resolution scrolling usually results in larger and/or more frequent mouse reports. This can result in overflow errors and overloading of the host computer's input buffer.
To deal with these issues, define `WHEEL_EXTENDED_REPORT` and throttle the rate at which mouse reports are sent.
:::
@@ -465,7 +465,7 @@ If there is a `_RIGHT` configuration option or callback, the [common configurati
:::
## Callbacks and Functions
## Callbacks and Functions
| Function | Description |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
@@ -513,7 +513,7 @@ To manually manipulate the mouse reports outside of the `pointing_device_task_*`
* `pointing_device_get_report()` - Returns the current report_mouse_t that represents the information sent to the host computer
* `pointing_device_set_report(report_mouse_t mouse_report)` - Overrides and saves the report_mouse_t to be sent to the host computer
* `pointing_device_send()` - Sends the mouse report to the host and zeroes out the report.
* `pointing_device_send()` - Sends the mouse report to the host and zeroes out the report.
When the mouse report is sent, the x, y, v, and h values are set to 0 (this is done in `pointing_device_send()`, which can be overridden to avoid this behavior). This way, button states persist, but movement will only occur once. For further customization, both `pointing_device_init` and `pointing_device_task` can be overridden.
@@ -548,7 +548,7 @@ Recall that the mouse report is set to zero (except the buttons) whenever it is
### Drag Scroll or Mouse Scroll
A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system.
A very common implementation is to use the mouse movement to scroll instead of moving the cursor on the system. This uses the `pointing_device_task_user` callback to intercept and modify the mouse report before it's sent to the host system.
```c
enum custom_keycodes {
@@ -575,7 +575,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
```
This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key.
This allows you to toggle between scrolling and cursor movement by pressing the DRAG_SCROLL key.
### Advanced Drag Scroll
@@ -711,7 +711,7 @@ If you are having issues with pointing device drivers debug messages can be enab
```c
#define POINTING_DEVICE_DEBUG
```
::: tip
The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](../faq_debug).
:::
@@ -720,7 +720,7 @@ The messages will be printed out to the `CONSOLE` output. For additional informa
---
# Automatic Mouse Layer {#pointing-device-auto-mouse}
When using a pointing device combined with a keyboard the mouse buttons are often kept on a separate layer from the default keyboard layer, which requires pressing or holding a key to change layers before using the mouse. To make this easier and more efficient an additional pointing device feature may be enabled that will automatically activate a target layer as soon as the pointing device is active _(in motion, mouse button pressed etc.)_ and deactivate the target layer after a set time.
When using a pointing device combined with a keyboard the mouse buttons are often kept on a separate layer from the default keyboard layer, which requires pressing or holding a key to change layers before using the mouse. To make this easier and more efficient an additional pointing device feature may be enabled that will automatically activate a target layer as soon as the pointing device is active _(in motion, mouse button pressed etc.)_ and deactivate the target layer after a set time.
Additionally if any key that is defined as a mouse key is pressed then the layer will be held as long as the key is pressed and the timer will be reset on key release. When a non-mouse key is pressed then the layer is deactivated early _(with some exceptions see below)_. Mod, mod tap, and one shot mod keys are ignored _(i.e. don't hold or activate layer but do not deactivate the layer either)_ when sending a modifier keycode _(e.g. hold for mod tap)_ allowing for mod keys to be used with the mouse without activating the target layer when typing.
@@ -754,9 +754,8 @@ void pointing_device_init_user(void) {
}
```
Because the auto mouse feature can be disabled/enabled during runtime and starts as disabled by default it must be enabled by calling `set_auto_mouse_enable(true);` somewhere in firmware before the feature will work.
_Note: for setting the target layer during initialization either setting `AUTO_MOUSE_DEFAULT_LAYER` in `config.h` or calling `set_auto_mouse_layer(<mouse_layer>)` can be used._
Because the auto mouse feature can be disabled/enabled during runtime and starts as disabled by default it must be enabled by calling `set_auto_mouse_enable(true);` somewhere in firmware before the feature will work.
_Note: for setting the target layer during initialization either setting `AUTO_MOUSE_DEFAULT_LAYER` in `config.h` or calling `set_auto_mouse_layer(<mouse_layer>)` can be used._
## How to Customize:
@@ -775,7 +774,7 @@ There are a few ways to control the auto mouse feature with both `config.h` opti
### Adding mouse keys
While all default mouse keys and layer keys(for current mouse layer) are treated as mouse keys, additional Keyrecords can be added to mouse keys by adding them to the is_mouse_record_* stack.
While all default mouse keys and layer keys(for current mouse layer) are treated as mouse keys, additional Keyrecords can be added to mouse keys by adding them to the is_mouse_record_* stack.
#### Callbacks for setting up additional key codes as mouse keys:
| Callback | Description |
@@ -783,7 +782,7 @@ While all default mouse keys and layer keys(for current mouse layer) are treated
| `bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record)` | keyboard level callback for adding mouse keys |
| `bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record)` | user/keymap level callback for adding mouse keys |
##### To use the callback function to add mouse keys:
##### To use the callback function to add mouse keys:
The following code will cause the enter key and all of the arrow keys to be treated as mouse keys (hold target layer while they are pressed and reset active layer timer).
```c
@@ -807,7 +806,7 @@ bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) {
There are several functions that allow for more advanced interaction with the auto mouse feature allowing for greater control.
### Functions to control auto mouse enable and target layer:
### Functions to control auto mouse enable and target layer:
| Function | Description | Aliases | Return type |
| :--------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------- | --------------: |
| `set_auto_mouse_enable(bool enable)` | Enable or disable auto mouse (true:enable, false:disable) | | `void`(None) |
@@ -826,27 +825,25 @@ There are several functions that allow for more advanced interaction with the au
| `get_auto_mouse_key_tracker(void)` | Gets the current count for the auto mouse key tracker. | | `int8_t` |
| `set_auto_mouse_key_tracker(int8_t key_tracker)` | Sets/Overrides the current count for the auto mouse key tracker. | | `void`(None) |
_NOTES:_
- _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._
- _It is recommended that `remove_auto_mouse_layer` is used in the `layer_state_set_*` stack of functions and `auto_mouse_layer_off` is used everywhere else_
- _`remove_auto_mouse_layer(state, false)` or `auto_mouse_layer_off()` should be called before any instance of `set_auto_mouse_enabled(false)` or `set_auto_mouse_layer(layer)` to ensure that the target layer will be removed appropriately before disabling auto mouse or changing target to avoid a stuck layer_
### Functions for handling custom key events:
_NOTES:_
- _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._
- _It is recommended that `remove_auto_mouse_layer` is used in the `layer_state_set_*` stack of functions and `auto_mouse_layer_off` is used everywhere else_
- _`remove_auto_mouse_layer(state, false)` or `auto_mouse_layer_off()` should be called before any instance of `set_auto_mouse_enabled(false)` or `set_auto_mouse_layer(layer)` to ensure that the target layer will be removed appropriately before disabling auto mouse or changing target to avoid a stuck layer_
### Functions for handling custom key events:
| Function | Description | Return type |
| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: |
| `auto_mouse_keyevent(bool pressed)` | Auto mouse mouse key event (true: key down, false: key up) | `void`(None) |
| `auto_mouse_trigger_reset(bool pressed)` | Reset auto mouse status on key down and start delay timer (non-mouse key event) | `void`(None) |
| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | `void`(None) |
| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | `bool` |
| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | `bool` |
_NOTE: Generally it would be preferable to use the `is_mouse_record_*` functions to add any additional keys that should act as mouse keys rather than adding `auto_mouse_keyevent(record.event->pressed)` to `process_records_*`_
### Advanced control examples
### Advanced control examples
#### Disable auto mouse on certain layers:
#### Disable auto mouse on certain layers:
The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_.
The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_.
```c
// in keymap.c:
@@ -885,7 +882,7 @@ layer_state_t default_layer_state_set_user(layer_state_t state) {
auto_mouse_layer_off();
set_auto_mouse_layer(_MOUSE_LAYER_2);
break;
default:
if((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_1) break;
auto_mouse_layer_off();
@@ -895,11 +892,9 @@ layer_state_t default_layer_state_set_user(layer_state_t state) {
}
```
### Use custom keys to control auto mouse:
Custom key records could also be created that control the auto mouse feature.
The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above.
### Use custom keys to control auto mouse:
Custom key records could also be created that control the auto mouse feature.
The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above.
```c
// in config.h:
@@ -928,11 +923,11 @@ bool process_record_user(uint16_t keycode, keyrecord_t* record) {
## Customize Target Layer Activation
Layer activation can be customized by overwriting the `auto_mouse_activation` function. This function is checked every time `pointing_device_task` is called when inactive and every `AUTO_MOUSE_DEBOUNCE` ms when active, and will evaluate pointing device level conditions that trigger target layer activation. When it returns true, the target layer will be activated barring the usual exceptions _(e.g. delay time has not expired)_.
Layer activation can be customized by overwriting the `auto_mouse_activation` function. This function is checked every time `pointing_device_task` is called when inactive and every `AUTO_MOUSE_DEBOUNCE` ms when active, and will evaluate pointing device level conditions that trigger target layer activation. When it returns true, the target layer will be activated barring the usual exceptions _(e.g. delay time has not expired)_.
By default it will return true if any of the `mouse_report` axes `x`,`y`,`h`,`v` are non zero, or if there is any mouse buttons active in `mouse_report`.
_Note: The Cirque pinnacle track pad already implements a custom activation function that will activate on touchdown as well as movement all of the default conditions, currently this only works for the master side of split keyboards._
| Function | Description | Return type |
| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: |
| `auto_mouse_activation(report_mouse_t mouse_report)` | Overwritable function that controls target layer activation (when true) | `bool` |
@@ -944,12 +939,12 @@ When using a custom pointing device (overwriting `pointing_device_task`) the fol
```c
bool pointing_device_task(void) {
//...Custom pointing device task code
// handle automatic mouse layer (needs report_mouse_t as input)
pointing_device_task_auto_mouse(local_mouse_report);
//...More custom pointing device task code
return pointing_device_send();
}
```

View File

@@ -5,7 +5,7 @@ Key after tapping the <kbd>Z</kbd> key types another "`z`." This is useful for
typing doubled letters, like the `z` in "`dazzle`": a double tap on <kbd>Z</kbd>
can instead be a roll from <kbd>Z</kbd> to <kbd>Repeat</kbd>, which is
potentially faster and more comfortable. The Repeat Key is also useful for
hotkeys, like repeating Ctrl + Shift + Right Arrow to select by word.
hotkeys, like repeating Ctrl + Shift + Right Arrow to select by word.
Repeat Key remembers mods that were active with the last key press. These mods
are combined with any additional mods while pressing the Repeat Key. If the last
@@ -49,10 +49,10 @@ reduce firmware size, Alternate Repeat may be disabled by adding in config.h:
The following alternate keys are defined by default. See
`get_alt_repeat_key_keycode_user()` below for how to change or add to these
definitions. Where it makes sense, these definitions also include combinations
definitions. Where it makes sense, these definitions also include combinations
with mods, like Ctrl + Left &harr; Ctrl + Right Arrow.
**Navigation**
**Navigation**
|Keycodes |Description |
|-----------------------------------|-----------------------------------|
@@ -65,7 +65,7 @@ with mods, like Ctrl + Left &harr; Ctrl + Right Arrow.
|`MS_WHLL` &harr; `MS_WHLR` | Mouse Wheel Left &harr; Right |
|`MS_WHLU` &harr; `MS_WHLD` | Mouse Wheel Up &harr; Down |
**Misc**
**Misc**
|Keycodes |Description |
|-----------------------------------|-----------------------------------|
@@ -73,7 +73,7 @@ with mods, like Ctrl + Left &harr; Ctrl + Right Arrow.
|`KC_LBRC` &harr; `KC_RBRC` | `[` &harr; `]` |
|`KC_LCBR` &harr; `KC_RCBR` | `{` &harr; `}` |
**Media**
**Media**
|Keycodes |Description |
|-----------------------------------|-----------------------------------|
@@ -176,9 +176,9 @@ macro](../feature_macros). This way macros can be used without having to
dedicate keys to them. The following defines a couple shortcuts.
* Typing <kbd>K</kbd>, <kbd>Alt Repeat</kbd> produces "`keyboard`," with the
initial "`k`" typed as usual and the "`eybord`" produced by the macro.
initial "`k`" typed as usual and the "`eybord`" produced by the macro.
* Typing <kbd>.</kbd>, <kbd>Alt Repeat</kbd> produces "`../`," handy for "up
directory" on the shell. Similarly, <kbd>.</kbd> types the initial "`.`" and
directory" on the shell. Similary, <kbd>.</kbd> types the initial "`.`" and
"`./`" is produced by the macro.
```c
@@ -290,7 +290,7 @@ By default, pressing the Repeat Key will simply behave as if the last key
were pressed again. This also works with macro keys with custom handlers,
invoking the macro again. In case fine-tuning is needed for sensible repetition,
you can handle how a key is repeated with `get_repeat_key_count()` within
`process_record_user()`.
`process_record_user()`.
The `get_repeat_key_count()` function returns a signed count of times the key
has been repeated or alternate repeated. When a key is pressed as usual,
@@ -306,16 +306,16 @@ bool process_record_user(uint16_t keycode, keyrecord_t* record) {
if (get_repeat_key_count() > 0) {
// MY_MACRO is being repeated!
if (record->event.pressed) {
SEND_STRING("repeat!");
SEND_STRING("repeat!");
}
} else {
} else {
// MY_MACRO is being used normally.
if (record->event.pressed) {
if (record->event.pressed) {
SEND_STRING("macro");
}
}
return false;
// Other macros...
}
return true;
@@ -347,19 +347,19 @@ bool process_record_user(uint16_t keycode, keyrecord_t* record) {
case MY_MACRO:
if (get_repeat_key_count() > 0) { // Repeating.
if (record->event.pressed) {
SEND_STRING("repeat!");
SEND_STRING("repeat!");
}
} else if (get_repeat_key_count() < 0) { // Alternate repeating.
if (record->event.pressed) {
SEND_STRING("alt repeat!");
}
} else { // Used normally.
if (record->event.pressed) {
if (record->event.pressed) {
SEND_STRING("macro");
}
}
return false;
// Other macros...
}
return true;
@@ -377,7 +377,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t* record) {
| `set_last_mods(mods)` | Set the mods to apply when repeating. |
| `get_repeat_key_count()` | Signed count of times the key has been repeated or alternate repeated. |
| `get_alt_repeat_key_keycode()` | Keycode to be used for alternate repeating. |
## Additional "Alternate" keys
@@ -437,7 +437,7 @@ static void process_altrep3(uint16_t keycode, uint8_t mods) {
bool process_record_user(uint16_t keycode, keyrecord_t* record) {
switch (keycode) {
case ALTREP2:
case ALTREP2:
if (record->event.pressed) {
process_altrep2(get_last_keycode(), get_last_mods());
}

View File

@@ -125,13 +125,13 @@ enum rgb_matrix_effects {
RGB_MATRIX_CYCLE_SPIRAL, // Full gradient spinning spiral around center of keyboard
RGB_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard
RGB_MATRIX_RAINBOW_BEACON, // Full tighter gradient spinning around center of keyboard
RGB_MATRIX_RAINBOW_PINWHEELS, // Full dual gradients spinning two halves of keyboard
RGB_MATRIX_RAINBOW_PINWHEELS, // Full dual gradients spinning two halfs of keyboard
RGB_MATRIX_FLOWER_BLOOMING, // Full tighter gradient of first half scrolling left to right and second half scrolling right to left
RGB_MATRIX_RAINDROPS, // Randomly changes a single key's hue
RGB_MATRIX_JELLYBEAN_RAINDROPS, // Randomly changes a single key's hue and saturation
RGB_MATRIX_HUE_BREATHING, // Hue shifts up a slight amount at the same time, then shifts back
RGB_MATRIX_HUE_PENDULUM, // Hue shifts up a slight amount in a wave to the right, then back to the left
RGB_MATRIX_HUE_WAVE, // Hue shifts up a slight amount and then back down in a wave to the right
RGB_MATRIX_HUE_BREATHING, // Hue shifts up a slight ammount at the same time, then shifts back
RGB_MATRIX_HUE_PENDULUM, // Hue shifts up a slight ammount in a wave to the right, then back to the left
RGB_MATRIX_HUE_WAVE, // Hue shifts up a slight ammount and then back down in a wave to the right
RGB_MATRIX_PIXEL_FRACTAL, // Single hue fractal filled keys pulsing horizontally out to edges
RGB_MATRIX_PIXEL_FLOW, // Pulsing RGB flow along LED wiring with random hues
RGB_MATRIX_PIXEL_RAIN, // Randomly light keys with random hues
@@ -240,7 +240,7 @@ In order to change the delay of temperature decrease define `RGB_MATRIX_TYPING_H
As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`.
Limit the distance the effect spreads to surrounding keys.
Limit the distance the effect spreads to surrounding keys.
```c
#define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40
@@ -406,8 +406,8 @@ const char* effect_name = rgb_matrix_get_mode_name(rgb_matrix_get_mode());
#define RGB_MATRIX_VAL_STEP 16 // The value by which to increment the brightness per adjustment action
#define RGB_MATRIX_SPD_STEP 16 // The value by which to increment the animation speed per adjustment action
#define RGB_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, if none has been set
#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
// If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
// If reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
#define RGB_TRIGGER_ON_KEYDOWN // Triggers RGB keypress events on key down. This makes RGB control feel more responsive. This may cause RGB to not function properly on some boards
```

View File

@@ -32,7 +32,7 @@ COMMAND_ENABLE = no
## Configuration
By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifier` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `SC_LSPO` key with `KC_TRNS` as the `Tap Modifier`.
By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifer` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `SC_LSPO` key with `KC_TRNS` as the `Tap Modifer`.
|Define |Default |Description |
|----------------|-------------------------------|---------------------------------------------------------------------------------|

View File

@@ -1,12 +1,12 @@
# Split Keyboard
# Split Keyboard
Many keyboards in the QMK Firmware repo are "split" keyboards. They use two controllers—one plugging into USB, and the second connected by a serial or an I<sup>2</sup>C connection over a TRRS or similar cable.
Many keyboards in the QMK Firmware repo are "split" keyboards. They use two controllers—one plugging into USB, and the second connected by a serial or an I<sup>2</sup>C connection over a TRRS or similar cable.
Split keyboards can have a lot of benefits, but there is some additional work needed to get them enabled.
Split keyboards can have a lot of benefits, but there is some additional work needed to get them enabled.
QMK Firmware has a generic implementation that is usable by any board, as well as numerous board specific implementations.
QMK Firmware has a generic implementation that is usable by any board, as well as numerous board specific implementations.
For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards.
For this, we will mostly be talking about the generic implementation used by the Let's Split and other keyboards.
::: warning
ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported.
@@ -29,33 +29,33 @@ Notes:
## Hardware Configuration
This assumes that you're using two Pro Micro-compatible controllers, and are using TRRS jacks to connect to two halves.
This assumes that you're using two Pro Micro-compatible controllers, and are using TRRS jacks to connect to two halves.
### Required Hardware
Apart from diodes and key switches for the keyboard matrix in each half, you will need 2x TRRS sockets and 1x TRRS cable.
Alternatively, you can use any sort of cable and socket that has at least 3 wires.
Alternatively, you can use any sort of cable and socket that has at least 3 wires.
If you want to use I<sup>2</sup>C to communicate between halves, you will need a cable with at least 4 wires and 2x 4.7kΩ pull-up resistors.
#### Considerations
#### Considerations
The most commonly used connection is a TRRS cable and jacks. These provide 4 wires, making them very useful for split keyboards, and are easy to find.
The most commonly used connection is a TRRS cable and jacks. These provide 4 wires, making them very useful for split keyboards, and are easy to find.
However, since one of the wires carries VCC, this means that the boards are not hot pluggable. You should always disconnect the board from USB before unplugging and plugging in TRRS cables, or you can short the controller, or worse.
However, since one of the wires carries VCC, this means that the boards are not hot pluggable. You should always disconnect the board from USB before unplugging and plugging in TRRS cables, or you can short the controller, or worse.
Another option is to use phone cables (as in, old school RJ-11/RJ-14 cables). Make sure that you use one that actually supports 4 wires/lanes.
Another option is to use phone cables (as in, old school RJ-11/RJ-14 cables). Make sure that you use one that actually supports 4 wires/lanes.
However, USB cables, SATA cables, and even just 4 wires have been known to be used for communication between the controllers.
However, USB cables, SATA cables, and even just 4 wires have been known to be used for communication between the controllers.
::: warning
Using USB cables for communication between the controllers works just fine, but the connector could be mistaken for a normal USB connection and potentially short out the keyboard, depending on how it's wired. For this reason, they are not recommended for connecting split keyboards.
Using USB cables for communication between the controllers works just fine, but the connector could be mistaken for a normal USB connection and potentially short out the keyboard, depending on how it's wired. For this reason, they are not recommended for connecting split keyboards.
:::
### Serial Wiring
The 3 wires of the TRS/TRRS cable need to connect GND, VCC, and D0/D1/D2/D3 (aka PD0/PD1/PD2/PD3) between the two Pro Micros.
The 3 wires of the TRS/TRRS cable need to connect GND, VCC, and D0/D1/D2/D3 (aka PD0/PD1/PD2/PD3) between the two Pro Micros.
::: tip
Note that the pin used here is actually set by `SOFT_SERIAL_PIN` below.
@@ -66,7 +66,7 @@ Note that the pin used here is actually set by `SOFT_SERIAL_PIN` below.
### I<sup>2</sup>C Wiring
The 4 wires of the TRRS cable need to connect GND, VCC, and SCL and SDA (aka PD0/pin 3 and PD1/pin 2, respectively) between the two Pro Micros.
The 4 wires of the TRRS cable need to connect GND, VCC, and SCL and SDA (aka PD0/pin 3 and PD1/pin 2, respectively) between the two Pro Micros.
The pull-up resistors may be placed on either half. If you wish to use the halves independently, it is also possible to use 4 resistors and have the pull-ups in both halves.
Note that the total resistance for the connected system should be within spec at 2.2k-10kOhm, with an 'ideal' at 4.7kOhm, regardless of the placement and number.
@@ -75,13 +75,13 @@ Note that the total resistance for the connected system should be within spec at
## Firmware Configuration
To enable the split keyboard feature, add the following to your `rules.mk`:
To enable the split keyboard feature, add the following to your `rules.mk`:
```make
SPLIT_KEYBOARD = yes
```
If you're using a custom transport (communication method), then you will also need to add:
If you're using a custom transport (communication method), then you will also need to add:
```make
SPLIT_TRANSPORT = custom
@@ -109,7 +109,7 @@ You can configure the firmware to read a pin on the controller to determine hand
#define SPLIT_HAND_PIN B7
```
This will read the specified pin. By default, if it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side.
This will read the specified pin. By default, if it's high, then the controller assumes it is the left hand, and if it's low, it's assumed to be the right side.
This behaviour can be flipped by adding this to you `config.h` file:
@@ -141,10 +141,10 @@ While `MATRIX_MASKED` isn't necessary to use `SPLIT_HAND_MATRIX_GRID` successful
#### Handedness by EEPROM
This method sets the keyboard's handedness by setting a flag in the persistent storage (`EEPROM`). This is checked when the controller first starts up, and determines what half the keyboard is, and how to orient the keyboard layout.
This method sets the keyboard's handedness by setting a flag in the persistent storage (`EEPROM`). This is checked when the controller first starts up, and determines what half the keyboard is, and how to orient the keyboard layout.
To enable this method, add the following to your `config.h` file:
To enable this method, add the following to your `config.h` file:
```c
#define EE_HANDS
@@ -176,7 +176,7 @@ Some controllers (e.g. Blackpill with DFU compatible bootloader) will need to be
[QMK Toolbox](https://github.com/qmk/qmk_toolbox/releases/) can also be used to flash EEPROM handedness files. Place the controller in bootloader mode and select menu option Tools -> EEPROM -> Set Left/Right Hand
:::
This setting is not changed when re-initializing the EEPROM using the `EE_CLR` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox](https://github.com/qmk/qmk_toolbox/releases/)'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files.
This setting is not changed when re-initializing the EEPROM using the `EE_CLR` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox](https://github.com/qmk/qmk_toolbox/releases/)'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files.
You can find the `EEPROM` files in the QMK firmware repo, [here](https://github.com/qmk/qmk_firmware/tree/master/quantum/split_common).
@@ -208,13 +208,13 @@ Because not every split keyboard is identical, there are a number of additional
#define USE_I2C
```
This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
This configures the use of I<sup>2</sup>C support for split keyboard transport (AVR only).
```c
#define SOFT_SERIAL_PIN D0
```
This sets the pin to be used for serial communication. If you're not using serial, you shouldn't need to define this.
This sets the pin to be used for serial communication. If you're not using serial, you shouldn't need to define this.
However, if you are using serial and I<sup>2</sup>C on the board, you will need to set this, and to something other than D0 and D1 (as these are used for I<sup>2</sup>C communication).
@@ -235,7 +235,7 @@ If you're having issues with serial communication, you can change this value, as
#define FORCED_SYNC_THROTTLE_MS 100
```
This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync.
This sets the maximum number of milliseconds before forcing a synchronization of data from master to slave. Under normal circumstances this sync occurs whenever the data _changes_, for safety a data transfer occurs after this number of milliseconds if no change has been detected since the last sync.
```c
#define SPLIT_MAX_CONNECTION_ERRORS 10
@@ -249,7 +249,7 @@ Set to 0 to disable the disconnection check altogether.
```
How long (in milliseconds) the master part should block all connection attempts to the slave after the communication has been flagged as disconnected (see `SPLIT_MAX_CONNECTION_ERRORS` above).
One communication attempt will be allowed every time this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again.
One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again.
Set to 0 to disable this throttling of communications while disconnected. This can save you a couple of bytes of firmware size.
@@ -280,7 +280,7 @@ This enables syncing of the Host LED status (caps lock, num lock, etc) between b
#define SPLIT_MODS_ENABLE
```
This enables transmitting modifier state (normal, weak, oneshot and oneshot locked) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifier state (e.g. displaying status on an OLED screen).
This enables transmitting modifier state (normal, weak, oneshot and oneshot locked) to the non primary side of the split keyboard. The purpose of this feature is to support cosmetic use of modifer state (e.g. displaying status on an OLED screen).
```c
#define SPLIT_WPM_ENABLE
@@ -304,7 +304,7 @@ This enables transmitting the current ST7565 on/off status to the slave side of
#define SPLIT_POINTING_ENABLE
```
This enables transmitting the pointing device status to the master side of the split keyboard. The purpose of this feature is to enable use pointing devices on the slave side.
This enables transmitting the pointing device status to the master side of the split keyboard. The purpose of this feature is to enable use pointing devices on the slave side.
::: warning
There is additional required configuration for `SPLIT_POINTING_ENABLE` outlined in the [pointing device documentation](pointing_device#split-keyboard-configuration).
@@ -401,7 +401,7 @@ By default, the inbound and outbound data is limited to a maximum of 32 bytes ea
### Hardware Configuration Options
There are some settings that you may need to configure, based on how the hardware is set up.
There are some settings that you may need to configure, based on how the hardware is set up.
```c
#define MATRIX_ROW_PINS_RIGHT { <row pins> }
@@ -433,7 +433,7 @@ This option enables synchronization of the RGB Light modes between the controlle
#define RGBLED_SPLIT { 6, 6 }
```
This sets how many LEDs are directly connected to each controller. The first number is the left side, and the second number is the right side.
This sets how many LEDs are directly connected to each controller. The first number is the left side, and the second number is the right side.
::: tip
This setting implies that `RGBLIGHT_SPLIT` is enabled, and will forcibly enable it, if it's not.
@@ -479,7 +479,7 @@ This set the maximum slave timeout when waiting for communication from master wh
Master/slave delegation is made either by detecting voltage on VBUS connection or waiting for USB communication (`SPLIT_USB_DETECT`). Pro Micro boards can use VBUS detection out of the box and be used with or without `SPLIT_USB_DETECT`.
Many ARM boards, but not all, do not support VBUS detection. Because it is common that ARM boards lack VBUS detection, `SPLIT_USB_DETECT` is automatically defined on ARM targets (technically when ChibiOS is targeted).
Many ARM boards, but not all, do not support VBUS detection. Because it is common that ARM boards lack VBUS detection, `SPLIT_USB_DETECT` is automatically defined on ARM targets (technically when ChibiOS is targetted).
### Teensy boards
@@ -501,7 +501,7 @@ You may need to use the 5V pad from the regulator block above as the pads were t
## Additional Resources
Nicinabox has a [very nice and detailed guide](https://github.com/nicinabox/lets-split-guide) for the Let's Split keyboard, that covers most everything you need to know, including troubleshooting information.
Nicinabox has a [very nice and detailed guide](https://github.com/nicinabox/lets-split-guide) for the Let's Split keyboard, that covers most everything you need to know, including troubleshooting information.
However, the RGB Light section is out of date, as it was written long before the RGB Split code was added to QMK Firmware. Instead, wire each strip up directly to the controller.

View File

@@ -182,12 +182,12 @@ void st7565_render(void);
void st7565_set_cursor(uint8_t col, uint8_t line);
// Advances the cursor to the next page, writing ' ' if true
// Wraps to the beginning when out of bounds
// Wraps to the begining when out of bounds
void st7565_advance_page(bool clearPageRemainder);
// Moves the cursor forward 1 character length
// Advance page if there is not enough room for the next character
// Wraps to the beginning when out of bounds
// Wraps to the begining when out of bounds
void st7565_advance_char(void);
// Writes a single character to the buffer at current cursor position

View File

@@ -102,7 +102,7 @@ STENO_ENABLE = yes
STENO_PROTOCOL = all
```
If you want to switch protocols programmatically, as part of a custom macro for example, don't use `tap_code(QK_STENO_*)`, as `tap_code` only supports [basic keycodes](../keycodes_basic). Instead, you should use `steno_set_mode(STENO_MODE_*)`, whose valid arguments are `STENO_MODE_BOLT` and `STENO_MODE_GEMINI`.
If you want to switch protocols programatically, as part of a custom macro for example, don't use `tap_code(QK_STENO_*)`, as `tap_code` only supports [basic keycodes](../keycodes_basic). Instead, you should use `steno_set_mode(STENO_MODE_*)`, whose valid arguments are `STENO_MODE_BOLT` and `STENO_MODE_GEMINI`.
The default protocol is Gemini PR but the last protocol used is stored in non-volatile memory so QMK will remember your choice between reboots of your keyboard &mdash; assuming that your keyboard features (emulated) EEPROM.

View File

@@ -162,7 +162,7 @@ The pins you'll absolutely have to avoid, as with any controller, are: GND, VCC,
Cut wires to the length of the distance from the a point on each column/row to the controller. You can solder anywhere along the row, as long as it's after the diode - soldering before the diode (on the keyswitch side) will cause that row not to work.
Ribbon cable can be used to keep this extra tidy. You may also want to consider routing the wires beneath the existing columns/rows.
Ribbon cable can be used to keep this extra tidy. You may also want to consider routing the wires beneath the exisiting columns/rows.
<img src="https://i.imgur.com/z2QlKfB.jpg" alt="Ribbon Cable" width="350"/>
@@ -188,7 +188,7 @@ qmk import-kbfirmware /path/to/export.json
For example:
```
$ qmk import-kbfirmware ~/Downloads/gh62.json
$ qmk import-kbfirmware ~/Downloads/gh62.json
Ψ Importing gh62.json.
⚠ Support here is basic - Consider using 'qmk new-keyboard' instead
@@ -227,16 +227,16 @@ If you've done all of these things, keep in mind that sometimes you might have h
## Finishing up
Once you have confirmed that the keyboard is working, if you have used a separate (non handwire specific) controller you will want to secure it in place. This can be done in many different ways e.g. hot glue, double sided sticky tape, 3D printed caddy, electrical tape.
Once you have confirmed that the keyboard is working, if you have used a seperate (non handwire specific) controller you will want to secure it in place. This can be done in many different ways e.g. hot glue, double sided sticky tape, 3D printed caddy, electrical tape.
If you found this fulfilling you could experiment by adding additional features such as [in switch LEDs](https://geekhack.org/index.php?topic=94258.0), [in switch RGB](https://www.reddit.com/r/MechanicalKeyboards/comments/5s1l5u/photoskeyboard_science_i_made_a_handwired_rgb/), [RGB underglow](https://medium.com/@DavidNZ/hand-wired-custom-keyboard-cdd14429c7b3#.7a1ovebsk) or even an [OLED display!](https://www.reddit.com/r/olkb/comments/5zy7og/adding_ssd1306_oled_display_to_your_build/)
If you found this fullfilling you could experiment by adding additional features such as [in switch LEDs](https://geekhack.org/index.php?topic=94258.0), [in switch RGB](https://www.reddit.com/r/MechanicalKeyboards/comments/5s1l5u/photoskeyboard_science_i_made_a_handwired_rgb/), [RGB underglow](https://medium.com/@DavidNZ/hand-wired-custom-keyboard-cdd14429c7b3#.7a1ovebsk) or even an [OLED display!](https://www.reddit.com/r/olkb/comments/5zy7og/adding_ssd1306_oled_display_to_your_build/)
There are a lot of possibilities inside the firmware - explore [the documentation](/) for a full feature list, and dive into the different keyboards to see how people use all of them. You can always stop by [the OLKB subreddit](https://reddit.com/r/olkb) or [QMK Discord](https://discord.gg/qmk) for help!
## Links to Other Guides
- [matt3o's step by step guide (BrownFox build)](https://deskthority.net/viewtopic.php?f=7&t=6050) also his [website](https://matt3o.com/hand-wiring-a-custom-keyboard/) and [video guide](https://www.youtube.com/watch?v=LVzpsjFWPP4)
- [Cribbit's "Modern hand wiring guide - stronger, cleaner, easier"](https://geekhack.org/index.php?topic=87689.0)
- [Cribbit's "Modern hand wiring guide - stronger, cleaner, easier"](https://geekhack.org/index.php?topic=87689.0)
- [Sasha Solomon's "Building my first Keyboard"](https://medium.com/@sachee/building-my-first-keyboard-and-you-can-too-512c0f8a4c5f)
- [RoastPotatoes' "How to hand wire a Planck"](https://blog.roastpotatoes.co/guide/2015/11/04/how-to-handwire-a-planck/)
- [Masterzen's "Handwired keyboard build log"](https://www.masterzen.fr/2018/12/16/handwired-keyboard-build-log-part-1/)

View File

@@ -24,7 +24,7 @@ Support for WS2811/WS2812{a,b,c} LED's. For more information see the [RGB Light]
## IS31FL3731
Support for up to 2 drivers. Each driver implements 2 charlieplex matrices to individually address LEDs using I2C. This allows up to 144 same color LEDs or 32 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](features/rgb_matrix) page.
Support for up to 2 drivers. Each driver impliments 2 charlieplex matrices to individually address LEDs using I2C. This allows up to 144 same color LEDs or 32 RGB LEDs. For more information on how to setup the driver see the [RGB Matrix](features/rgb_matrix) page.
## IS31FL3733

View File

@@ -50,90 +50,64 @@ You will need to install [MSYS2](https://www.msys2.org). Once installed, close a
Install the QMK CLI by running:
```sh
pacman --needed --noconfirm --disable-download-timeout -S git mingw-w64-x86_64-python-qmk
curl -fsSL https://install.qmk.fm | sh
```
::::
==== macOS
QMK maintains a Homebrew tap and formula which will automatically install the CLI and all necessary dependencies.
#### Prerequisites
You will need to install Homebrew. Follow the instructions on https://brew.sh.
::: tip
If you are using an Apple Silicon machine, the installation process will take significantly longer because GitHub actions do not have native runners to build binary packages for the ARM and AVR toolchains.
:::
#### Installation
Install the QMK CLI by running:
```sh
brew install qmk/qmk/qmk
curl -fsSL https://install.qmk.fm | sh
```
==== Linux/WSL
#### Installation
::: info
Many Linux distributions are supported, but not all. Mainstream distributions will have best success -- if possible, choose either Debian or its derivatives (such as Ubuntu, or Mint), CentOS or its derivatives (such as Fedora, or Rocky Linux), and Arch or its derivatives (such as Manjaro, or CachyOS).
:::
Install the QMK CLI by running:
```sh
curl -fsSL https://install.qmk.fm | sh
```
::: tip
**Note for WSL users**: By default, the installation process will clone the QMK repository into your WSL home directory, but if you have cloned manually, ensure that it is located inside the WSL instance instead of the Windows filesystem (ie. not in `/mnt`), as accessing it is currently [extremely slow](https://github.com/microsoft/WSL/issues/4197).
:::
#### Prerequisites
You will need to install Git and Python. It's very likely that you already have both, but if not, one of the following commands should install them:
* Debian / Ubuntu / Devuan: `sudo apt install -y git python3-pip`
* Fedora / Red Hat / CentOS: `sudo yum -y install git python3-pip`
* Arch / Manjaro: `sudo pacman --needed --noconfirm -S git python-pip libffi`
* Void: `sudo xbps-install -y git python3-pip`
* Solus: `sudo eopkg -y install git python3`
* Sabayon: `sudo equo install dev-vcs/git dev-python/pip`
* Gentoo: `sudo emerge dev-vcs/git dev-python/pip`
#### Installation
Install the QMK CLI by running:
```sh
python3 -m pip install --user qmk
```
Alternatively, install the QMK CLI as a [uv](https://docs.astral.sh/uv/) managed tool, kept isolated in a virtual environment (requires uv to be installed):
```sh
uv tool install qmk
```
#### Community Packages
These packages are maintained by community members, so may not be up to date or completely functional. If you encounter problems, please report them to their respective maintainers.
On Arch-based distros you can install the CLI from the official repositories (NOTE: at the time of writing this package marks some dependencies as optional that should not be):
```sh
sudo pacman -S qmk
```
You can also try the `qmk-git` package from AUR:
```sh
yay -S qmk-git
```
::: warning
Any QMK packages provided by your distribution's package manager are almost certainly out of date. It is strongly suggested the installation script above is used instead.
:::
==== FreeBSD
#### Installation
::: warning
FreeBSD support is provided on a best-effort basis by the community instead of the QMK maintainers. It is strongly suggested that you use either Windows, macOS, or a supported distribution of Linux instead.
:::
Install the FreeBSD package for QMK CLI by running:
```sh
pkg install -g "py*-qmk"
```
NOTE: remember to follow the instructions printed at the end of installation (use `pkg info -Dg "py*-qmk"` to show them again).
::: info NOTE
Remember to follow the instructions printed at the end of installation (use `pkg info -Dg "py*-qmk"` to show them again).
:::
:::::

View File

@@ -5,7 +5,7 @@ LVGL (Light and Versatile Graphics Library) is an open-source graphics library p
LVGL integrates with [Quantum Painter's](quantum_painter) API and drivers to render to the display, the hardware supported by Quantum Painter is also supported by LVGL.
::: tip
Keep in mind that enabling the LVGL integration has a big impact in firmware size, it is recommended to use a supported MCU with >256 kB of flash space.
Keep in mind that enabling the LVGL integration has a big impact in firmware size, it is recommeded to use a supported MCU with >256 kB of flash space.
:::
To learn more about LVGL and how to use it please take a look at their [official documentation](https://docs.lvgl.io/8.2/intro/)
@@ -35,7 +35,7 @@ static painter_device_t display;
void keyboard_post_init_kb(void) {
display = qp_make_.......; // Create the display
qp_init(display, QP_ROTATION_0); // Initialise the display
if (qp_lvgl_attach(display)) { // Attach LVGL to the display
...Your code to draw // Run LVGL specific code to draw
}

View File

@@ -5,14 +5,14 @@ AVR is severely resource-constrained, and as QMK continues to grow, it is approa
However, if you need to reduce the compiled size of your firmware to fit the controller's limited flash size, there are a number of options to do so.
## `rules.mk` Settings
First and foremost is enabling link time optimization. To do so, add this to your rules.mk:
First and foremost is enabling link time optimization. To do so, add this to your rules.mk:
```make
LTO_ENABLE = yes
```
This will cause the final step to take longer, but should get you a smaller compiled size. This also disables Action Functions, and Action Macros, both of which are deprecated.
This will get you the most savings, in most situations.
From there, disabling extraneous systems will help -- e.g.:
From there, disabling extraneous systems will help -- e.g.:
```make
CONSOLE_ENABLE = no
COMMAND_ENABLE = no
@@ -21,10 +21,10 @@ EXTRAKEY_ENABLE = no
```
This disables some of the functionality that you may not need. But note that extrakeys disables stuff like the media keys and system volume control.
If that isn't enough to get your firmware down to size, then there are some additional features that you can disable:
If that isn't enough to get your firmware down to size, then there are some additional features that you can disable:
```make
SPACE_CADET_ENABLE = no
GRAVE_ESC_ENABLE = no
GRAVE_ESC_ENABLE = no
MAGIC_ENABLE = no
```
These features are enabled by default, but they may not be needed. Double check to make sure. The [Magic Keycodes](keycodes_magic) are the largest and control things like NKRO toggling, GUI and ALT/CTRL swapping, etc. Disabling them will disable those functions. See [Magic Functions](#magic-functions) for disabling related functions.
@@ -51,7 +51,7 @@ Starting with Lock Key support. If you have a Cherry MX Lock switch (lucky you!)
#undef LOCKING_SUPPORT_ENABLE
#undef LOCKING_RESYNC_ENABLE
```
Oneshots. If you're not using these, you can disable the feature by adding this to your `config.h`:
Oneshots. If you're not using these, you can disable the feature by adding this to your `config.h`:
```c
#define NO_ACTION_ONESHOT
```
@@ -61,7 +61,7 @@ The same with tapping keys (mod tap, layer tap, etc)
```
## Audio Settings
If you're using the Audio feature, by default that includes the music mode feature. This translates matrix positions into notes. It's neat for sure, but most likely, you're not using it. You can disable it by adding this to your `config.h`:
If you're using the Audio feature, by default that includes the music mode feature. This tranlates matrix positions into notes. It's neat for sure, but most likely, you're not using it. You can disable it by adding this to your `config.h`:
```c
#define NO_MUSIC_MODE
```
@@ -124,7 +124,7 @@ into this:
oled_write_P(PSTR("WPM: "), false);
oled_write(get_u8_str(get_current_wpm(), ' '), false);
```
which outputs `WPM: 5`. Or this:
which outputs `WPM: 5`. Or this:
```c
// NEW CODE
oled_write_P(PSTR("WPM: "), false);

View File

@@ -58,7 +58,7 @@ USB NKRO methods
Report Format
-------------
Other report formats than following are possible, though these format are typical one.
Other report formats than followings are possible, though these format are typical one.
1. Standard 8bytes
modifiers(bitmap) 1byte

View File

@@ -116,12 +116,12 @@ void st7565_render(void);
void st7565_set_cursor(uint8_t col, uint8_t line);
// Advances the cursor to the next page, writing ' ' if true
// Wraps to the beginning when out of bounds
// Wraps to the begining when out of bounds
void st7565_advance_page(bool clearPageRemainder);
// Moves the cursor forward 1 character length
// Advance page if there is not enough room for the next character
// Wraps to the beginning when out of bounds
// Wraps to the begining when out of bounds
void st7565_advance_char(void);
// Writes a single character to the buffer at current cursor position

View File

@@ -1,89 +0,0 @@
{
"keyboard_name": "The Clog",
"manufacturer": "S'mores",
"url": "https://github.com/smores56/clog",
"maintainer": "@smores56",
"usb": {
"vid": "0xBEEF",
"pid": "0x5051",
"device_version": "0.0.1"
},
"development_board": "elite_c",
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"nkro": false
},
"matrix_pins": {
"direct": [
["B2", "F7", "F6", "F5", "F4"],
["B6", "B3", "B1", "D3", "D1"],
["D0", "D4", "C6", "D7", "E6"],
["B4", "B5", null, null, null]
]
},
"split": {
"bootmagic": {
"matrix": [4, 4]
},
"enabled": true,
"matrix_pins": {
"right": {
"direct": [
["F4", "F5", "F6", "F7", "B2"],
["D1", "D3", "B1", "B3", "B6"],
["E6", "D7", "C6", "D4", "D0"],
["B5", "B4", null, null, null]
]
}
},
"serial": {
"pin": "D2"
}
},
"community_layouts": ["split_3x5_2"],
"layout_aliases": {
"LAYOUT": "LAYOUT_split_3x5_2"
},
"layouts": {
"LAYOUT_split_3x5_2": {
"layout": [
{"label": "L01", "matrix": [0, 0], "x": 0, "y": 2.27},
{"label": "L02", "matrix": [0, 1], "x": 2, "y": 0.31},
{"label": "L03", "matrix": [0, 2], "x": 3, "y": 0},
{"label": "L04", "matrix": [0, 3], "x": 4, "y": 0.28},
{"label": "L05", "matrix": [0, 4], "x": 5, "y": 0.42},
{"label": "R01", "matrix": [4, 0], "x": 9, "y": 0.42},
{"label": "R02", "matrix": [4, 1], "x": 10, "y": 0.28},
{"label": "R03", "matrix": [4, 2], "x": 11, "y": 0},
{"label": "R04", "matrix": [4, 3], "x": 12, "y": 0.31},
{"label": "R05", "matrix": [4, 4], "x": 14, "y": 2.27},
{"label": "L06", "matrix": [1, 0], "x": 0, "y": 2.27},
{"label": "L07", "matrix": [1, 1], "x": 2, "y": 1.31},
{"label": "L08", "matrix": [1, 2], "x": 3, "y": 1},
{"label": "L09", "matrix": [1, 3], "x": 4, "y": 1.28},
{"label": "L10", "matrix": [1, 4], "x": 5, "y": 1.42},
{"label": "R06", "matrix": [5, 0], "x": 9, "y": 1.42},
{"label": "R07", "matrix": [5, 1], "x": 10, "y": 1.28},
{"label": "R08", "matrix": [5, 2], "x": 11, "y": 1},
{"label": "R09", "matrix": [5, 3], "x": 12, "y": 1.31},
{"label": "R10", "matrix": [5, 4], "x": 14, "y": 2.27},
{"label": "L11", "matrix": [2, 0], "x": 0, "y": 3.27},
{"label": "L12", "matrix": [2, 1], "x": 2, "y": 2.31},
{"label": "L13", "matrix": [2, 2], "x": 3, "y": 2},
{"label": "L14", "matrix": [2, 3], "x": 4, "y": 2.28},
{"label": "L15", "matrix": [2, 4], "x": 5, "y": 2.42},
{"label": "R11", "matrix": [6, 0], "x": 9, "y": 2.42},
{"label": "R12", "matrix": [6, 1], "x": 10, "y": 2.28},
{"label": "R13", "matrix": [6, 2], "x": 11, "y": 2},
{"label": "R14", "matrix": [6, 3], "x": 12, "y": 2.31},
{"label": "R15", "matrix": [6, 4], "x": 14, "y": 3.27},
{"label": "L16", "matrix": [3, 0], "x": 5, "y": 3.9},
{"label": "L17", "matrix": [3, 1], "x": 6, "y": 3.7},
{"label": "R16", "matrix": [7, 0], "x": 8, "y": 3.7},
{"label": "R17", "matrix": [7, 1], "x": 9, "y": 3.9}
]
}
}
}

View File

@@ -1,7 +0,0 @@
// Copyright 2024 Sam Mohr (@smores56)
// SPDX-License-Identifier: GPL-2.0+
#pragma once
// Defaults for usable home row mods
#define TAPPING_TERM 250

View File

@@ -1,35 +0,0 @@
// Copyright 2024 Sam Mohr (@smores56)
// SPDX-License-Identifier: GPL-2.0+
#include QMK_KEYBOARD_H
/* Base layer 0 layout uses home row mods. See the following guide for details:
* https://precondition.github.io/home-row-mods
*/
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_split_3x5_2(
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
CTL_T(KC_A),ALT_T(KC_S),GUI_T(KC_D),SFT_T(KC_F), KC_G, KC_H, SFT_T(KC_J),GUI_T(KC_K),ALT_T(KC_L),CTL_T(KC_SCLN),
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
LT(2,KC_TAB), KC_ENT, KC_SPC, LT(1,KC_BSPC)
),
[1] = LAYOUT_split_3x5_2(
KC_INS, KC_1, KC_2, KC_3, KC_VOLU, KC_HOME, KC_PGDN, KC_PGUP, KC_END, KC_DQUO,
KC_DEL, KC_4, KC_5, KC_6, KC_VOLD, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_QUOT,
KC_CAPS, KC_7, KC_8, KC_9, KC_0, _______, _______, _______, _______, _______,
MO(3), QK_GESC, _______, _______
),
[2] = LAYOUT_split_3x5_2(
_______, KC_LBRC, KC_LCBR, KC_RCBR, _______, KC_CIRC, KC_LPRN, KC_RPRN, KC_RBRC, KC_TILD,
KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_AMPR, KC_MINS, KC_EQL, KC_BSLS, KC_GRV,
_______, _______, _______, _______, _______, KC_ASTR, KC_UNDS, KC_PLUS, KC_PIPE, _______,
_______, _______, KC_DEL, MO(3)
),
[3] = LAYOUT_split_3x5_2(
_______, KC_F1, KC_F2, KC_F3, KC_F10, _______, MS_WHLU, MS_WHLD, _______, QK_BOOT,
_______, KC_F4, KC_F5, KC_F6, KC_F11, MS_LEFT, MS_DOWN, MS_UP, MS_RGHT, KC_INS,
_______, KC_F7, KC_F8, KC_F9, KC_F12, _______, MS_BTN1, MS_BTN2, _______, _______,
_______, _______, _______, _______
)
};

View File

@@ -1,26 +0,0 @@
# The Clog
This is the official firmware for a family of boards designed by [smores56](https://github.com/smores56),
the first of which was [the Clog](https://github.com/smores56/clog). This firmware works for the following boards:
- [The Clog](https://github.com/smores56/clog)
- [The Clog V3](https://github.com/smores56/clog-v3)
- [The Steel Toe](https://github.com/smores56/steel-toe)
- [The Sephirette](https://github.com/smores56/sephirette)
Make example for this keyboard (after setting up your build environment):
make clog:default
Flashing example for this keyboard:
make clog:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Bootloader
Enter the bootloader in 2 ways:
* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@@ -1,23 +0,0 @@
/* Copyright 2025 Zackarias Montell (@Wholteza)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define SERIAL_USART_FULL_DUPLEX
#define SERIAL_USART_TX_PIN GP0
#define SERIAL_USART_RX_PIN GP1
#define MASTER_RIGHT

View File

@@ -1,107 +0,0 @@
{
"manufacturer": "zacke.dev",
"keyboard_name": "5x6_split",
"maintainer": "Wholteza",
"board": "GENERIC_RP_RP2040",
"bootloader": "rp2040",
"diode_direction": "COL2ROW",
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"nkro": true
},
"matrix_pins": {
"cols": ["GP16", "GP17", "GP18", "GP19", "GP20", "GP21"],
"rows": ["GP15", "GP14", "GP13", "GP12", "GP11"]
},
"processor": "RP2040",
"split": {
"enabled": true,
"matrix_pins": {
"right": {
"cols": ["GP10", "GP11", "GP12", "GP13", "GP14", "GP15"],
"rows": ["GP16", "GP17", "GP18", "GP19", "GP20"]
}
},
"serial": {
"driver": "vendor"
},
"transport": {
"watchdog": true,
"watchdog_timeout": 3000
}
},
"url": "https://git.zacke.dev/wholteza/5x6-split-kb/src/branch/main/rev1",
"usb": {
"device_version": "1.0.0",
"pid": "0x0057",
"vid": "0xFEED"
},
"layouts": {
"LAYOUT": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 1], "x": 1, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [5, 0], "x": 6, "y": 0},
{"matrix": [5, 1], "x": 7, "y": 0},
{"matrix": [5, 2], "x": 8, "y": 0},
{"matrix": [5, 3], "x": 9, "y": 0},
{"matrix": [5, 4], "x": 10, "y": 0},
{"matrix": [5, 5], "x": 11, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1},
{"matrix": [1, 1], "x": 1, "y": 1},
{"matrix": [1, 2], "x": 2, "y": 1},
{"matrix": [1, 3], "x": 3, "y": 1},
{"matrix": [1, 4], "x": 4, "y": 1},
{"matrix": [1, 5], "x": 5, "y": 1},
{"matrix": [6, 0], "x": 6, "y": 1},
{"matrix": [6, 1], "x": 7, "y": 1},
{"matrix": [6, 2], "x": 8, "y": 1},
{"matrix": [6, 3], "x": 9, "y": 1},
{"matrix": [6, 4], "x": 10, "y": 1},
{"matrix": [6, 5], "x": 11, "y": 1},
{"matrix": [2, 0], "x": 0, "y": 2},
{"matrix": [2, 1], "x": 1, "y": 2},
{"matrix": [2, 2], "x": 2, "y": 2},
{"matrix": [2, 3], "x": 3, "y": 2},
{"matrix": [2, 4], "x": 4, "y": 2},
{"matrix": [2, 5], "x": 5, "y": 2},
{"matrix": [7, 0], "x": 6, "y": 2},
{"matrix": [7, 1], "x": 7, "y": 2},
{"matrix": [7, 2], "x": 8, "y": 2},
{"matrix": [7, 3], "x": 9, "y": 2},
{"matrix": [7, 4], "x": 10, "y": 2},
{"matrix": [7, 5], "x": 11, "y": 2},
{"matrix": [3, 0], "x": 0, "y": 3},
{"matrix": [3, 1], "x": 1, "y": 3},
{"matrix": [3, 2], "x": 2, "y": 3},
{"matrix": [3, 3], "x": 3, "y": 3},
{"matrix": [3, 4], "x": 4, "y": 3},
{"matrix": [3, 5], "x": 5, "y": 3},
{"matrix": [8, 0], "x": 6, "y": 3},
{"matrix": [8, 1], "x": 7, "y": 3},
{"matrix": [8, 2], "x": 8, "y": 3},
{"matrix": [8, 3], "x": 9, "y": 3},
{"matrix": [8, 4], "x": 10, "y": 3},
{"matrix": [8, 5], "x": 11, "y": 3},
{"matrix": [4, 0], "x": 0, "y": 4},
{"matrix": [4, 1], "x": 1, "y": 4},
{"matrix": [4, 2], "x": 2, "y": 4},
{"matrix": [4, 3], "x": 3, "y": 4},
{"matrix": [4, 4], "x": 4, "y": 4},
{"matrix": [4, 5], "x": 5, "y": 4},
{"matrix": [9, 0], "x": 6, "y": 4},
{"matrix": [9, 1], "x": 7, "y": 4},
{"matrix": [9, 2], "x": 8, "y": 4},
{"matrix": [9, 3], "x": 9, "y": 4},
{"matrix": [9, 4], "x": 10, "y": 4},
{"matrix": [9, 5], "x": 11, "y": 4}
]
}
}
}

View File

@@ -1,46 +0,0 @@
/* Copyright 2025 Zackarias Montell (@Wholteza)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum layer_names {
_BASE,
_LOWER,
_RAISE
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE] = LAYOUT(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, 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_BSPC,
KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
_______, KC_LCTL, KC_LALT, KC_LGUI, MO(1), KC_SPC, KC_SPC, MO(2), KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
[_LOWER] = LAYOUT(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC,
KC_GRV, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, KC_PGUP, KC_PGDN, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
[_RAISE] = LAYOUT(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC,
KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, S(KC_NUHS), S(KC_NUBS), KC_HOME, KC_END, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MNXT, KC_VOLD, KC_VOLU, QK_BOOT
)
};

View File

@@ -1,33 +0,0 @@
# 5x6_split revision 1
![5x6_split-rev1](https://i.imgur.com/FJ3kro3.jpeg)
**This is the first revision of the 5x6_split keyboard.**
The 5x6_split is designed to be easily accessible, with openly and freely available hardware and firmware. It features a split layout to minimize the PCB footprint, reducing manufacturing costs. Like the Ergodox, it allows you to keep your hands at shoulder width for a more natural typing posture, but without a thumb cluster, making it a better option for users who experience discomfort or limitations in their thumbs.
The keyboard is also designed with ergonomics in mind. Its low profile, measuring just 19.4 mm from the table to the top of the keycaps (including rubber feet), helps reduce wrist strain during extended use.
In the repository linked below (see the Hardware Availability section), you will find KiCad schematics, PCB designs, and 3D models for a printable chassis.
* Keyboard Maintainer: [wholteza](https://github.com/wholteza)
* Hardware Supported: Raspberry Pi Pico, 5x6_split PCB, 5x6_split Chassis
* Hardware Availability: [PCB, Chassi 3d-models](https://git.zacke.dev/wholteza/5x6-split-kb/src/branch/main/rev1)
Make example for this keyboard (after setting up your build environment):
make handwired/5x6_split/rev1:default
Flashing example for this keyboard:
make handwired/5x6_split/rev1:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Bootloader
Enter the bootloader in 3 ways:
* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard
* **Physical reset button**: Briefly press the button on the back of the PCB - some may have pads you must short instead
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@@ -1,77 +0,0 @@
{
"manufacturer": "OLKB",
"keyboard_name": "Planck",
"maintainer": "Wholteza <zackarias@montell.se>",
"bootloader": "halfkay",
"diode_direction": "COL2ROW",
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"nkro": true
},
"matrix_pins": {
"cols": ["C7", "D6", "D7", "B4", "B5", "B6", "F7", "F6", "F5", "F4", "F1", "F0"],
"rows": ["E6", "B3", "B7", "D0"]
},
"processor": "atmega32u4",
"url": "https://blog.roastpotatoes.co/guide/2015/11/04/how-to-handwire-a-planck/",
"usb": {
"device_version": "1.0.0",
"pid": "0x0053",
"vid": "0xFEED"
},
"layouts": {
"LAYOUT": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 1], "x": 1, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [0, 6], "x": 6, "y": 0},
{"matrix": [0, 7], "x": 7, "y": 0},
{"matrix": [0, 8], "x": 8, "y": 0},
{"matrix": [0, 9], "x": 9, "y": 0},
{"matrix": [0, 10], "x": 10, "y": 0},
{"matrix": [0, 11], "x": 11, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1},
{"matrix": [1, 1], "x": 1, "y": 1},
{"matrix": [1, 2], "x": 2, "y": 1},
{"matrix": [1, 3], "x": 3, "y": 1},
{"matrix": [1, 4], "x": 4, "y": 1},
{"matrix": [1, 5], "x": 5, "y": 1},
{"matrix": [1, 6], "x": 6, "y": 1},
{"matrix": [1, 7], "x": 7, "y": 1},
{"matrix": [1, 8], "x": 8, "y": 1},
{"matrix": [1, 9], "x": 9, "y": 1},
{"matrix": [1, 10], "x": 10, "y": 1},
{"matrix": [1, 11], "x": 11, "y": 1},
{"matrix": [2, 0], "x": 0, "y": 2},
{"matrix": [2, 1], "x": 1, "y": 2},
{"matrix": [2, 2], "x": 2, "y": 2},
{"matrix": [2, 3], "x": 3, "y": 2},
{"matrix": [2, 4], "x": 4, "y": 2},
{"matrix": [2, 5], "x": 5, "y": 2},
{"matrix": [2, 6], "x": 6, "y": 2},
{"matrix": [2, 7], "x": 7, "y": 2},
{"matrix": [2, 8], "x": 8, "y": 2},
{"matrix": [2, 9], "x": 9, "y": 2},
{"matrix": [2, 10], "x": 10, "y": 2},
{"matrix": [2, 11], "x": 11, "y": 2},
{"matrix": [3, 0], "x": 0, "y": 3},
{"matrix": [3, 1], "x": 1, "y": 3},
{"matrix": [3, 2], "x": 2, "y": 3},
{"matrix": [3, 3], "x": 3, "y": 3},
{"matrix": [3, 4], "x": 4, "y": 3},
{"matrix": [3, 5], "x": 5, "y": 3},
{"matrix": [3, 7], "x": 7, "y": 3},
{"matrix": [3, 8], "x": 8, "y": 3},
{"matrix": [3, 9], "x": 9, "y": 3},
{"matrix": [3, 10], "x": 10, "y": 3},
{"matrix": [3, 11], "x": 11, "y": 3}
]
}
}
}

View File

@@ -1,43 +0,0 @@
/* Copyright 2025 Zackarias Montel (@Wholteza)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum layer_names {
_BASE,
_NUM,
_SYM
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_BASE] = LAYOUT(
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC,
KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
MO(3), KC_LCTL, KC_LALT, KC_LGUI, MO(1), KC_SPC, MO(2), KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
[_NUM] = LAYOUT(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, KC_PGUP, KC_PGDN, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
[_SYM] = LAYOUT(
KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
KC_TRNS, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, S(KC_NUHS), S(KC_NUBS), KC_HOME, KC_END, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MNXT, KC_VOLD, KC_VOLU, QK_BOOT
)
};

View File

@@ -1,27 +0,0 @@
# Plank Handwired
![plank handwired](https://i.imgur.com/khseWs7.jpeg)
A compact 40% (12x4) ortholinear keyboard kit sold by OLKB before the PCB was released.
Keyboard Maintainer: [Wholteza](https://github.com/wholteza)
Hardware Supported: [Teensy 2.0](https://www.pjrc.com/store/teensy.html)
Hardware Availability: [PJRC](https://www.pjrc.com/store/teensy.html)
Make example for this keyboard (after setting up your build environment):
make handwired/planck:default
Flashing example for this keyboard:
make handwired/planck:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Bootloader
Enter the bootloader in 3 ways:
* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (The left key closest to the USB Port) and plug in the keyboard
* **Physical reset button**: Briefly press the button on the back of the PCB - some may have pads you must short instead
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@@ -1,8 +0,0 @@
{
"manufacturer": "Keebio",
"maintainer": "Keebio",
"url": "https://keeb.io",
"usb": {
"vid": "0xCB10"
}
}

View File

@@ -1,35 +0,0 @@
// Copyright 2025 Keebio (@keebio)
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_ortho_5x12(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, 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_DEL,
KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
MO(3), KC_LCTL, KC_LALT, KC_LGUI, TL_LOWR, KC_SPC, KC_SPC, TL_UPPR, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
[1] = LAYOUT_ortho_5x12(
KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC,
KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_DEL,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
RM_NEXT, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12,S(KC_NUHS),S(KC_NUBS),KC_HOME,KC_END, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
[2] = LAYOUT_ortho_5x12(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_DEL,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, KC_PGUP, KC_PGDN, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
[3] = LAYOUT_ortho_5x12(
_______, QK_BOOT, RM_TOGG, RM_NEXT, RM_HUED, RM_HUEU, RM_SATD, RM_SATU, RM_VALD, RM_VALU, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
)
};

View File

@@ -1 +0,0 @@
TRI_LAYER_ENABLE = yes

View File

@@ -1,24 +0,0 @@
# Nyquist LM
A low-profile 60% split 5x12 ortholinear keyboard made and sold by Keebio. [More info at Keebio](https://keeb.io).
* Keyboard Maintainer: [Bakingpy/nooges](https://github.com/nooges)
* Hardware Availability: [Keebio](https://keeb.io/)
Make example for this keyboard (after setting up your build environment):
make keebio/nyquist_lm/rev1:default
Example of flashing this keyboard:
make keebio/nyquist_lm/rev1:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Bootloader
Enter the bootloader in 3 ways:
* **Bootmagic reset**: Hold down the key at the top left of left half or top right of right half and plug in the keyboard
* **Physical reset button**: Press and hold the button on the back of the PCB for at least 1 second and let go
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@@ -1,23 +0,0 @@
// Copyright 2025 Keebio (@keebio)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
/* Defines for the split keyboard setup */
#define SERIAL_USART_DRIVER SD1 // USART 1
#define SERIAL_USART_TX_PIN A9
#define SERIAL_USART_RX_PIN A10
#define SERIAL_USART_TX_PAL_MODE 7
#define SERIAL_USART_RX_PAL_MODE 7
#define SERIAL_USART_FULL_DUPLEX
#define SERIAL_USART_PIN_SWAP
#define USB_VBUS_PIN A7
/* Defines for the RGB matrix */
#define WS2812_PWM_DRIVER PWMD3
#define WS2812_PWM_CHANNEL 3
#define WS2812_PWM_PAL_MODE 2
#define WS2812_DMA_STREAM STM32_DMA1_STREAM2
#define WS2812_DMA_CHANNEL 2
#define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM3_UP

View File

@@ -1,10 +0,0 @@
// Copyright 2025 Keebio (@keebio)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define HAL_USE_SERIAL TRUE
#define HAL_USE_PWM TRUE
#include_next <halconf.h>

View File

@@ -1,304 +0,0 @@
{
"keyboard_name": "Nyquist LM Rev. 1",
"bootloader": "stm32-dfu",
"diode_direction": "COL2ROW",
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"rgb_matrix": true
},
"matrix_pins": {
"cols": ["A6", "A5", "A4", "A15", "B5", "B6"],
"rows": ["B3", "B4", "B7", "F0", "F1"]
},
"processor": "STM32G431",
"rgb_matrix": {
"animations": {
"alphas_mods": true,
"gradient_up_down": true,
"gradient_left_right": true,
"breathing": true,
"band_sat": true,
"band_val": true,
"band_pinwheel_sat": true,
"band_pinwheel_val": true,
"band_spiral_sat": true,
"band_spiral_val": true,
"cycle_all": true,
"cycle_left_right": true,
"cycle_up_down": true,
"cycle_out_in": true,
"cycle_out_in_dual": true,
"rainbow_moving_chevron": true,
"cycle_pinwheel": true,
"cycle_spiral": true,
"dual_beacon": true,
"rainbow_beacon": true,
"rainbow_pinwheels": true,
"flower_blooming": true,
"raindrops": true,
"jellybean_raindrops": true,
"hue_breathing": true,
"hue_pendulum": true,
"hue_wave": true,
"pixel_fractal": true,
"pixel_flow": true,
"pixel_rain": true,
"typing_heatmap": true,
"digital_rain": true,
"solid_reactive_simple": true,
"solid_reactive": true,
"solid_reactive_wide": true,
"solid_reactive_multiwide": true,
"solid_reactive_cross": true,
"solid_reactive_multicross": true,
"solid_reactive_nexus": true,
"solid_reactive_multinexus": true,
"splash": true,
"multisplash": true,
"solid_splash": true,
"solid_multisplash": true,
"starlight": true,
"starlight_smooth": true,
"starlight_dual_hue": true,
"starlight_dual_sat": true,
"riverflow": true
},
"driver": "ws2812",
"layout": [
{ "flags": 4, "matrix": [0, 0], "x": 9, "y": 6 },
{ "flags": 4, "matrix": [0, 1], "x": 28, "y": 6 },
{ "flags": 4, "matrix": [0, 2], "x": 46, "y": 6 },
{ "flags": 4, "matrix": [0, 3], "x": 65, "y": 6 },
{ "flags": 4, "matrix": [0, 4], "x": 84, "y": 6 },
{ "flags": 4, "matrix": [0, 5], "x": 102, "y": 6 },
{ "flags": 4, "matrix": [1, 5], "x": 102, "y": 19 },
{ "flags": 4, "matrix": [1, 4], "x": 84, "y": 19 },
{ "flags": 4, "matrix": [1, 3], "x": 65, "y": 19 },
{ "flags": 4, "matrix": [1, 2], "x": 46, "y": 19 },
{ "flags": 4, "matrix": [1, 1], "x": 28, "y": 19 },
{ "flags": 4, "matrix": [1, 0], "x": 9, "y": 19 },
{ "flags": 4, "matrix": [2, 0], "x": 9, "y": 32 },
{ "flags": 4, "matrix": [2, 1], "x": 28, "y": 32 },
{ "flags": 4, "matrix": [2, 2], "x": 46, "y": 32 },
{ "flags": 4, "matrix": [2, 3], "x": 65, "y": 32 },
{ "flags": 4, "matrix": [2, 4], "x": 84, "y": 32 },
{ "flags": 4, "matrix": [2, 5], "x": 102, "y": 32 },
{ "flags": 4, "matrix": [3, 5], "x": 102, "y": 44 },
{ "flags": 4, "matrix": [3, 4], "x": 84, "y": 44 },
{ "flags": 4, "matrix": [3, 3], "x": 65, "y": 44 },
{ "flags": 4, "matrix": [3, 2], "x": 46, "y": 44 },
{ "flags": 4, "matrix": [3, 1], "x": 28, "y": 44 },
{ "flags": 4, "matrix": [3, 0], "x": 9, "y": 44 },
{ "flags": 4, "matrix": [4, 0], "x": 9, "y": 57 },
{ "flags": 4, "matrix": [4, 1], "x": 28, "y": 57 },
{ "flags": 4, "matrix": [4, 2], "x": 46, "y": 57 },
{ "flags": 4, "matrix": [4, 3], "x": 65, "y": 57 },
{ "flags": 4, "matrix": [4, 4], "x": 81, "y": 57 },
{ "flags": 4, "matrix": [4, 5], "x": 102, "y": 57 },
{ "flags": 4, "matrix": [5, 0], "x": 121, "y": 6 },
{ "flags": 4, "matrix": [5, 1], "x": 140, "y": 6 },
{ "flags": 4, "matrix": [5, 2], "x": 158, "y": 6 },
{ "flags": 4, "matrix": [5, 3], "x": 177, "y": 6 },
{ "flags": 4, "matrix": [5, 4], "x": 196, "y": 6 },
{ "flags": 4, "matrix": [5, 5], "x": 214, "y": 6 },
{ "flags": 4, "matrix": [6, 5], "x": 214, "y": 19 },
{ "flags": 4, "matrix": [6, 4], "x": 196, "y": 19 },
{ "flags": 4, "matrix": [6, 3], "x": 177, "y": 19 },
{ "flags": 4, "matrix": [6, 2], "x": 158, "y": 19 },
{ "flags": 4, "matrix": [6, 1], "x": 140, "y": 19 },
{ "flags": 4, "matrix": [6, 0], "x": 121, "y": 19 },
{ "flags": 4, "matrix": [7, 0], "x": 121, "y": 32 },
{ "flags": 4, "matrix": [7, 1], "x": 140, "y": 32 },
{ "flags": 4, "matrix": [7, 2], "x": 158, "y": 32 },
{ "flags": 4, "matrix": [7, 3], "x": 177, "y": 32 },
{ "flags": 4, "matrix": [7, 4], "x": 196, "y": 32 },
{ "flags": 4, "matrix": [7, 5], "x": 214, "y": 32 },
{ "flags": 4, "matrix": [8, 5], "x": 214, "y": 44 },
{ "flags": 4, "matrix": [8, 4], "x": 196, "y": 44 },
{ "flags": 4, "matrix": [8, 3], "x": 177, "y": 44 },
{ "flags": 4, "matrix": [8, 2], "x": 158, "y": 44 },
{ "flags": 4, "matrix": [8, 1], "x": 140, "y": 44 },
{ "flags": 4, "matrix": [8, 0], "x": 121, "y": 44 },
{ "flags": 4, "matrix": [9, 0], "x": 121, "y": 57 },
{ "flags": 4, "matrix": [9, 1], "x": 140, "y": 57 },
{ "flags": 4, "matrix": [9, 2], "x": 158, "y": 57 },
{ "flags": 4, "matrix": [9, 3], "x": 177, "y": 57 },
{ "flags": 4, "matrix": [9, 4], "x": 196, "y": 57 },
{ "flags": 4, "matrix": [9, 5], "x": 214, "y": 57 }
],
"max_brightness": 160,
"sleep": true,
"split_count": [30, 30]
},
"split": {
"bootmagic": {
"matrix": [5, 5]
},
"enabled": true,
"handedness": {
"pin": "A8"
},
"serial": {
"driver": "usart"
},
"transport": {
"sync": {
"matrix_state": true
}
}
},
"usb": {
"device_version": "1.0.0",
"pid": "0x1856"
},
"ws2812": {
"driver": "pwm",
"pin": "B0"
},
"community_layouts": ["ortho_5x12", "ortho_4x12"],
"layouts": {
"LAYOUT_ortho_5x12": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 1], "x": 1, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [5, 0], "x": 7, "y": 0},
{"matrix": [5, 1], "x": 8, "y": 0},
{"matrix": [5, 2], "x": 9, "y": 0},
{"matrix": [5, 3], "x": 10, "y": 0},
{"matrix": [5, 4], "x": 11, "y": 0},
{"matrix": [5, 5], "x": 12, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1},
{"matrix": [1, 1], "x": 1, "y": 1},
{"matrix": [1, 2], "x": 2, "y": 1},
{"matrix": [1, 3], "x": 3, "y": 1},
{"matrix": [1, 4], "x": 4, "y": 1},
{"matrix": [1, 5], "x": 5, "y": 1},
{"matrix": [6, 0], "x": 7, "y": 1},
{"matrix": [6, 1], "x": 8, "y": 1},
{"matrix": [6, 2], "x": 9, "y": 1},
{"matrix": [6, 3], "x": 10, "y": 1},
{"matrix": [6, 4], "x": 11, "y": 1},
{"matrix": [6, 5], "x": 12, "y": 1},
{"matrix": [2, 0], "x": 0, "y": 2},
{"matrix": [2, 1], "x": 1, "y": 2},
{"matrix": [2, 2], "x": 2, "y": 2},
{"matrix": [2, 3], "x": 3, "y": 2},
{"matrix": [2, 4], "x": 4, "y": 2},
{"matrix": [2, 5], "x": 5, "y": 2},
{"matrix": [7, 0], "x": 7, "y": 2},
{"matrix": [7, 1], "x": 8, "y": 2},
{"matrix": [7, 2], "x": 9, "y": 2},
{"matrix": [7, 3], "x": 10, "y": 2},
{"matrix": [7, 4], "x": 11, "y": 2},
{"matrix": [7, 5], "x": 12, "y": 2},
{"matrix": [3, 0], "x": 0, "y": 3},
{"matrix": [3, 1], "x": 1, "y": 3},
{"matrix": [3, 2], "x": 2, "y": 3},
{"matrix": [3, 3], "x": 3, "y": 3},
{"matrix": [3, 4], "x": 4, "y": 3},
{"matrix": [3, 5], "x": 5, "y": 3},
{"matrix": [8, 0], "x": 7, "y": 3},
{"matrix": [8, 1], "x": 8, "y": 3},
{"matrix": [8, 2], "x": 9, "y": 3},
{"matrix": [8, 3], "x": 10, "y": 3},
{"matrix": [8, 4], "x": 11, "y": 3},
{"matrix": [8, 5], "x": 12, "y": 3},
{"matrix": [4, 0], "x": 0, "y": 4},
{"matrix": [4, 1], "x": 1, "y": 4},
{"matrix": [4, 2], "x": 2, "y": 4},
{"matrix": [4, 3], "x": 3, "y": 4},
{"matrix": [4, 4], "x": 4, "y": 4},
{"matrix": [4, 5], "x": 5, "y": 4},
{"matrix": [9, 0], "x": 7, "y": 4},
{"matrix": [9, 1], "x": 8, "y": 4},
{"matrix": [9, 2], "x": 9, "y": 4},
{"matrix": [9, 3], "x": 10, "y": 4},
{"matrix": [9, 4], "x": 11, "y": 4},
{"matrix": [9, 5], "x": 12, "y": 4}
]
},
"LAYOUT_ortho_4x12": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 1], "x": 1, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [0, 4], "x": 4, "y": 0},
{"matrix": [0, 5], "x": 5, "y": 0},
{"matrix": [5, 0], "x": 7, "y": 0},
{"matrix": [5, 1], "x": 8, "y": 0},
{"matrix": [5, 2], "x": 9, "y": 0},
{"matrix": [5, 3], "x": 10, "y": 0},
{"matrix": [5, 4], "x": 11, "y": 0},
{"matrix": [5, 5], "x": 12, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1},
{"matrix": [1, 1], "x": 1, "y": 1},
{"matrix": [1, 2], "x": 2, "y": 1},
{"matrix": [1, 3], "x": 3, "y": 1},
{"matrix": [1, 4], "x": 4, "y": 1},
{"matrix": [1, 5], "x": 5, "y": 1},
{"matrix": [6, 0], "x": 7, "y": 1},
{"matrix": [6, 1], "x": 8, "y": 1},
{"matrix": [6, 2], "x": 9, "y": 1},
{"matrix": [6, 3], "x": 10, "y": 1},
{"matrix": [6, 4], "x": 11, "y": 1},
{"matrix": [6, 5], "x": 12, "y": 1},
{"matrix": [2, 0], "x": 0, "y": 2},
{"matrix": [2, 1], "x": 1, "y": 2},
{"matrix": [2, 2], "x": 2, "y": 2},
{"matrix": [2, 3], "x": 3, "y": 2},
{"matrix": [2, 4], "x": 4, "y": 2},
{"matrix": [2, 5], "x": 5, "y": 2},
{"matrix": [7, 0], "x": 7, "y": 2},
{"matrix": [7, 1], "x": 8, "y": 2},
{"matrix": [7, 2], "x": 9, "y": 2},
{"matrix": [7, 3], "x": 10, "y": 2},
{"matrix": [7, 4], "x": 11, "y": 2},
{"matrix": [7, 5], "x": 12, "y": 2},
{"matrix": [3, 0], "x": 0, "y": 3},
{"matrix": [3, 1], "x": 1, "y": 3},
{"matrix": [3, 2], "x": 2, "y": 3},
{"matrix": [3, 3], "x": 3, "y": 3},
{"matrix": [3, 4], "x": 4, "y": 3},
{"matrix": [3, 5], "x": 5, "y": 3},
{"matrix": [8, 0], "x": 7, "y": 3},
{"matrix": [8, 1], "x": 8, "y": 3},
{"matrix": [8, 2], "x": 9, "y": 3},
{"matrix": [8, 3], "x": 10, "y": 3},
{"matrix": [8, 4], "x": 11, "y": 3},
{"matrix": [8, 5], "x": 12, "y": 3}
]
}
}
}

View File

@@ -1,14 +0,0 @@
// Copyright 2025 Keebio (@keebio)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include_next <mcuconf.h>
/* enable USART1, used for split comms */
#undef STM32_SERIAL_USE_USART1
#define STM32_SERIAL_USE_USART1 TRUE
/* enable TIM3, used for RGB LED PWM driver */
#undef STM32_PWM_USE_TIM3
#define STM32_PWM_USE_TIM3 TRUE

View File

@@ -1,11 +0,0 @@
// Copyright 2025 Keebio (@keebio)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
void keyboard_pre_init_kb(void) {
// Disable the PD peripheral in pre-init because its pins (B4, B6) are being used in the matrix:
PWR->CR3 |= PWR_CR3_UCPD_DBDIS;
// Call the corresponding _user() function (see https://docs.qmk.fm/#/custom_quantum_functions)
keyboard_pre_init_user();
}

View File

@@ -1,6 +0,0 @@
// Copyright 2025 takashicompany (@takashicompany)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define EE_HANDS

View File

@@ -1,80 +0,0 @@
{
"manufacturer": "takashicompany",
"keyboard_name": "Palmbrain",
"maintainer": "takashicompany",
"development_board": "promicro",
"dynamic_keymap": {
"layer_count": 12
},
"features": {
"bootmagic": true,
"extrakey": true,
"mousekey": true,
"nkro": true
},
"matrix_pins": {
"direct": [
["F5", "D1", "F6", "E6", "F7", "F4", "D0", "C6", "B4", "B1", "D3", "D4", "D7", "B5", "B3", "B2", "B6"]
]
},
"split": {
"enabled": true,
"matrix_pins": {
"right": {
"direct": [
["F7", "E6", "F6", "D1", "F5", "B1", "B4", "C6", "D0", "F4", "B3", "B5", "D7", "D4", "D3", "B6", "B2"]
]
}
},
"serial": {
"pin": "D2"
}
},
"url": "https://github.com/takashicompany/palmbrain",
"usb": {
"device_version": "1.0.0",
"pid": "0x0066",
"vid": "0x7463"
},
"community_layouts": ["split_3x5_2"],
"layouts": {
"LAYOUT_split_3x5_2": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0.45},
{"matrix": [0, 1], "x": 0.9, "y": 0.225},
{"matrix": [0, 2], "x": 1.8, "y": 0},
{"matrix": [0, 3], "x": 2.7, "y": 0.225},
{"matrix": [0, 4], "x": 3.6, "y": 0.45},
{"matrix": [1, 0], "x": 7.2, "y": 0.45},
{"matrix": [1, 1], "x": 8.1, "y": 0.225},
{"matrix": [1, 2], "x": 9, "y": 0},
{"matrix": [1, 3], "x": 9.9, "y": 0.225},
{"matrix": [1, 4], "x": 10.8, "y": 0.45},
{"matrix": [0, 5], "x": 0, "y": 1.35},
{"matrix": [0, 6], "x": 0.9, "y": 1.125},
{"matrix": [0, 7], "x": 1.8, "y": 0.9},
{"matrix": [0, 8], "x": 2.7, "y": 1.125},
{"matrix": [0, 9], "x": 3.6, "y": 1.35},
{"matrix": [1, 5], "x": 7.2, "y": 1.35},
{"matrix": [1, 6], "x": 8.1, "y": 1.125},
{"matrix": [1, 7], "x": 9, "y": 0.9},
{"matrix": [1, 8], "x": 9.9, "y": 1.125},
{"matrix": [1, 9], "x": 10.8, "y": 1.35},
{"matrix": [0, 10], "x": 0, "y": 2.25},
{"matrix": [0, 11], "x": 0.9, "y": 2.025},
{"matrix": [0, 12], "x": 1.8, "y": 1.8},
{"matrix": [0, 13], "x": 2.7, "y": 2.025},
{"matrix": [0, 14], "x": 3.6, "y": 2.25},
{"matrix": [1, 10], "x": 7.2, "y": 2.25},
{"matrix": [1, 11], "x": 8.1, "y": 2.025},
{"matrix": [1, 12], "x": 9, "y": 1.8},
{"matrix": [1, 13], "x": 9.9, "y": 2.025},
{"matrix": [1, 14], "x": 10.8, "y": 2.25},
{"matrix": [0, 15], "x": 3.6, "y": 3.375},
{"matrix": [0, 16], "x": 4.5, "y": 3.375},
{"matrix": [1, 15], "x": 6.3, "y": 3.375},
{"matrix": [1, 16], "x": 7.2, "y": 3.375}
]
}
}
}

View File

@@ -1,49 +0,0 @@
// Copyright 2025 takashicompany (@takashicompany)
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_split_3x5_2(
LT(4, KC_Q), KC_W, KC_E, LT(3, KC_R), KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
KC_A, KC_S, LT(4, KC_D), KC_F, KC_G, KC_H, KC_J, LT(3, KC_K), KC_L, KC_ENT,
LSFT_T(KC_Z), LGUI_T(KC_X), KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, LCTL_T(KC_DOT), KC_BSPC,
LALT_T(KC_LNG2), LSFT_T(KC_TAB), LT(2, KC_SPC), LT(1, KC_LNG1)
),
[1] = LAYOUT_split_3x5_2(
KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0,
LCTL_T(KC_EQL), KC_LBRC, KC_SLSH, KC_MINS, KC_INT1, KC_SCLN, KC_QUOT, KC_RBRC, KC_NUHS, KC_INT3,
LSFT_T(KC_PLUS), KC_LCBR, KC_QUES, KC_UNDS, LSFT(KC_INT1), KC_COLN, KC_DQUO, KC_RCBR, LSFT(KC_NUHS), LSFT(KC_INT3),
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[2] = LAYOUT_split_3x5_2(
KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, LGUI(KC_INT3),
KC_PLUS, KC_LCBR, KC_QUES, KC_UNDS, LSFT(KC_INT1), KC_COLN, KC_DQUO, KC_RCBR, LSFT(KC_NUHS), LSFT(KC_INT3),
KC_LSFT, KC_LGUI, KC_LALT, KC_LNG2, KC_LSFT, KC_SPC, KC_LNG1, KC_TRNS, KC_TRNS, KC_DEL,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[3] = LAYOUT_split_3x5_2(
KC_ESC, KC_TAB, KC_UP, KC_NO, KC_NO, KC_NO, KC_NO, KC_UP, KC_NO, KC_NO,
KC_LCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_NO, KC_NO, KC_LEFT, KC_DOWN, KC_RGHT, KC_NO,
KC_LSFT, KC_LGUI, KC_LALT, KC_LNG2, KC_TRNS, KC_NO, KC_LNG1, KC_NO, KC_NO, KC_DEL,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[4] = LAYOUT_split_3x5_2(
KC_NO, KC_TAB, KC_NO, KC_NO, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6,
KC_NO, KC_NO, KC_NO, KC_NO, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12,
KC_LSFT, KC_NO, KC_NO, KC_NO, KC_TRNS, KC_TRNS, KC_TRNS, KC_NO, MO(5), KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[5] = LAYOUT_split_3x5_2(
UG_TOGG, UG_NEXT, UG_HUEU, UG_SATU, UG_VALU, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_TRNS, KC_TRNS, KC_TRNS, KC_NO, KC_NO, QK_BOOT, KC_NO, KC_NO, KC_NO, KC_NO,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -1,31 +0,0 @@
# Palmbrain
![takashicompany/Palmbrain](https://i.imgur.com/Og9lYip.jpeg)
Palmbrain is a 34-key split keyboard with a key pitch of 17 mm (0.9 u).
It can be equipped with BLE Micro Pro and is also compatible with wireless connectivity.
Palmbrainは17mm(0.9u)のキーピッチを採用した34キーの分割型のキーボードです。
BLE Micro Proを搭載することが可能で、無線化にも対応しています。
* Keyboard Maintainer: [takashicompany](https://github.com/takashicompany)
* Hardware Supported: PCB, Pro Micro development board.
* Hardware Availability: https://github.com/takashicompany/palmbrain
Make example for this keyboard (after setting up your build environment):
make takashicompany/palmbrain:default
Flashing example for this keyboard:
make takashicompany/palmbrain:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Bootloader
Enter the bootloader in 3 ways:
* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard
* **Physical reset button**: Briefly press the button on the back of the PCB - some may have pads you must short instead
* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available

View File

@@ -3,6 +3,8 @@
We list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.
"""
import os
import platform
import platformdirs
import shlex
import sys
from importlib.util import find_spec
@@ -12,6 +14,12 @@ from subprocess import run
from milc import cli, __VERSION__
from milc.questions import yesno
# Ensure the QMK distribution is on the `$PATH` if present.
_default_distrib_path = cli.run(['cygpath', '-w', '/opt/qmk']).stdout.strip() if 'windows' in platform.platform().lower() else platformdirs.user_data_dir('qmk') # this must be kept in sync with the default values inside `util/env-bootstrap.sh`!
QMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _default_distrib_path))
if QMK_DISTRIB_DIR.exists():
os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH']
import_names = {
# A mapping of package name to importable name
'pep8-naming': 'pep8ext_naming',

View File

@@ -1,7 +1,6 @@
"""Check for specific programs.
"""
from enum import Enum
import re
import shutil
from subprocess import DEVNULL, TimeoutExpired
from tempfile import TemporaryDirectory
@@ -18,6 +17,10 @@ class CheckStatus(Enum):
ESSENTIAL_BINARIES = {
'make': {},
'git': {},
'dos2unix': {},
'diff': {},
'dfu-programmer': {},
'avrdude': {},
'dfu-util': {},
@@ -30,14 +33,39 @@ ESSENTIAL_BINARIES = {
}
def _parse_gcc_version(version):
m = re.match(r"(\d+)(?:\.(\d+))?(?:\.(\d+))?", version)
def _check_make_version():
last_line = ESSENTIAL_BINARIES['make']['output'].split('\n')[0]
version_number = last_line.split()[2]
cli.log.info('Found make version %s', version_number)
return {
'major': int(m.group(1)),
'minor': int(m.group(2)) if m.group(2) else 0,
'patch': int(m.group(3)) if m.group(3) else 0,
}
return CheckStatus.OK
def _check_git_version():
last_line = ESSENTIAL_BINARIES['git']['output'].split('\n')[0]
version_number = last_line.split()[2]
cli.log.info('Found git version %s', version_number)
return CheckStatus.OK
def _check_dos2unix_version():
last_line = ESSENTIAL_BINARIES['dos2unix']['output'].split('\n')[0]
version_number = last_line.split()[1]
cli.log.info('Found dos2unix version %s', version_number)
return CheckStatus.OK
def _check_diff_version():
last_line = ESSENTIAL_BINARIES['diff']['output'].split('\n')[0]
if 'Apple diff' in last_line:
version_number = last_line
else:
version_number = last_line.split()[3]
cli.log.info('Found diff version %s', version_number)
return CheckStatus.OK
def _check_arm_gcc_version():
@@ -148,16 +176,24 @@ def check_binaries():
"""Iterates through ESSENTIAL_BINARIES and tests them.
"""
ok = CheckStatus.OK
missing_from_path = []
for binary in sorted(ESSENTIAL_BINARIES):
try:
if not is_executable(binary):
if not is_in_path(binary):
ok = CheckStatus.ERROR
missing_from_path.append(binary)
elif not is_executable(binary):
ok = CheckStatus.ERROR
except TimeoutExpired:
cli.log.debug('Timeout checking %s', binary)
if ok != CheckStatus.ERROR:
ok = CheckStatus.WARNING
if missing_from_path:
location_noun = 'its location' if len(missing_from_path) == 1 else 'their locations'
cli.log.error('{fg_red}' + ', '.join(missing_from_path) + f' may need to be installed, or {location_noun} added to your path.')
return ok
@@ -165,6 +201,10 @@ def check_binary_versions():
"""Check the versions of ESSENTIAL_BINARIES
"""
checks = {
'make': _check_make_version,
'git': _check_git_version,
'dos2unix': _check_dos2unix_version,
'diff': _check_diff_version,
'arm-none-eabi-gcc': _check_arm_gcc_version,
'avr-gcc': _check_avr_gcc_version,
'avrdude': _check_avrdude_version,
@@ -196,15 +236,18 @@ def check_submodules():
return CheckStatus.OK
def is_executable(command):
"""Returns True if command exists and can be executed.
def is_in_path(command):
"""Returns True if command is found in the path.
"""
# Make sure the command is in the path.
res = shutil.which(command)
if res is None:
if shutil.which(command) is None:
cli.log.error("{fg_red}Can't find %s in your path.", command)
return False
return True
def is_executable(command):
"""Returns True if command can be executed.
"""
# Make sure the command can be executed
version_arg = ESSENTIAL_BINARIES[command].get('version_arg', '--version')
check = cli.run([command, version_arg], combined_output=True, stdin=DEVNULL, timeout=5)

View File

@@ -16,6 +16,60 @@ from qmk.commands import in_virtualenv
from qmk.userspace import qmk_userspace_paths, qmk_userspace_validate, UserspaceValidationError
def distrib_tests():
def _load_kvp_file(file):
"""Load a simple key=value file into a dictionary
"""
vars = {}
with open(file, 'r') as f:
for line in f:
if '=' in line:
key, value = line.split('=', 1)
vars[key.strip()] = value.strip()
return vars
def _parse_toolchain_release_file(file):
"""Parse the QMK toolchain release info file
"""
try:
vars = _load_kvp_file(file)
return f'{vars.get("TOOLCHAIN_HOST", "unknown")}:{vars.get("TOOLCHAIN_TARGET", "unknown")}:{vars.get("COMMIT_HASH", "unknown")}'
except Exception as e:
cli.log.warning('Error reading QMK toolchain release info file: %s', e)
return f'Unknown toolchain release info file: {file}'
def _parse_flashutils_release_file(file):
"""Parse the QMK flashutils release info file
"""
try:
vars = _load_kvp_file(file)
return f'{vars.get("FLASHUTILS_HOST", "unknown")}:{vars.get("COMMIT_HASH", "unknown")}'
except Exception as e:
cli.log.warning('Error reading QMK flashutils release info file: %s', e)
return f'Unknown flashutils release info file: {file}'
try:
from qmk.cli import QMK_DISTRIB_DIR
if (QMK_DISTRIB_DIR / 'etc').exists():
cli.log.info('Found QMK tools distribution directory: {fg_cyan}%s', QMK_DISTRIB_DIR)
toolchains = [_parse_toolchain_release_file(file) for file in (QMK_DISTRIB_DIR / 'etc').glob('toolchain_release_*')]
if len(toolchains) > 0:
cli.log.info('Found QMK toolchains: {fg_cyan}%s', ', '.join(toolchains))
else:
cli.log.warning('No QMK toolchains manifest found.')
flashutils = [_parse_flashutils_release_file(file) for file in (QMK_DISTRIB_DIR / 'etc').glob('flashutils_release_*')]
if len(flashutils) > 0:
cli.log.info('Found QMK flashutils: {fg_cyan}%s', ', '.join(flashutils))
else:
cli.log.warning('No QMK flashutils manifest found.')
except ImportError:
cli.log.info('QMK tools distribution not found.')
return CheckStatus.OK
def os_tests():
"""Determine our OS and run platform specific tests
"""
@@ -128,6 +182,7 @@ def doctor(cli):
cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE)
status = os_status = os_tests()
distrib_tests()
userspace_tests(None)

View File

@@ -16,7 +16,7 @@ from qmk.build_targets import BuildTarget, JsonKeymapBuildTarget
from qmk.util import maybe_exit_config
def mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool, no_temp: bool, parallel: int, print_failures: bool, **env):
def mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool, no_temp: bool, parallel: int, **env):
if len(targets) == 0:
return
@@ -37,30 +37,6 @@ def mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool,
builddir.mkdir(parents=True, exist_ok=True)
with open(makefile, "w") as f:
# yapf: disable
f.write(
f"""\
# This file is auto-generated by qmk mass-compile
# Do not edit this file directly.
all: print_failures
.PHONY: all_targets print_failures
print_failures: all_targets
"""# noqa
)
if print_failures:
f.write(
f"""\
@for f in $$(ls .build/failed.log.{os.getpid()}.* 2>/dev/null | sort); do \\
echo; \\
echo "======================================================================================"; \\
echo "Failed build log: $$f"; \\
echo "------------------------------------------------------"; \\
cat $$f; \\
echo "------------------------------------------------------"; \\
done
"""# noqa
)
# yapf: enable
for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)):
keyboard_name = target.keyboard
keymap_name = target.keymap
@@ -82,7 +58,7 @@ print_failures: all_targets
f.write(
f"""\
.PHONY: {target_filename}{target_suffix}_binary
all_targets: {target_filename}{target_suffix}_binary
all: {target_filename}{target_suffix}_binary
{target_filename}{target_suffix}_binary:
@rm -f "{build_log}" || true
@echo "Compiling QMK Firmware for target: '{keyboard_name}:{keymap_name}'..." >>"{build_log}"
@@ -122,7 +98,6 @@ all_targets: {target_filename}{target_suffix}_binary
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the commands to be run.")
@cli.argument('-p', '--print-failures', arg_only=True, action='store_true', help="Print failed builds.")
@cli.argument(
'-f',
'--filter',
@@ -148,4 +123,4 @@ def mass_compile(cli):
else:
targets = search_keymap_targets([('all', cli.config.mass_compile.keymap)], cli.args.filter)
return mass_compile_targets(targets, cli.args.clean, cli.args.dry_run, cli.args.no_temp, cli.config.mass_compile.parallel, cli.args.print_failures, **build_environment(cli.args.env))
return mass_compile_targets(targets, cli.args.clean, cli.args.dry_run, cli.args.no_temp, cli.config.mass_compile.parallel, **build_environment(cli.args.env))

View File

@@ -20,7 +20,6 @@ def _extra_arg_setter(target, extra_args):
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the commands to be run.")
@cli.argument('-p', '--print-failures', arg_only=True, action='store_true', help="Print failed builds.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
@cli.subcommand('Compiles the build targets specified in userspace `qmk.json`.')
def userspace_compile(cli):
@@ -43,4 +42,4 @@ def userspace_compile(cli):
if len(keyboard_keymap_targets) > 0:
build_targets.extend(search_keymap_targets(keyboard_keymap_targets))
return mass_compile_targets(list(set(build_targets)), cli.args.clean, cli.args.dry_run, cli.config.userspace_compile.no_temp, cli.config.userspace_compile.parallel, cli.args.print_failures, **build_environment(cli.args.env))
return mass_compile_targets(list(set(build_targets)), cli.args.clean, cli.args.dry_run, cli.config.userspace_compile.no_temp, cli.config.userspace_compile.parallel, **build_environment(cli.args.env))

View File

@@ -175,8 +175,9 @@ def keyboard_completer(prefix, action, parser, parsed_args):
return list_keyboards()
@lru_cache(maxsize=None)
def list_keyboards():
"""Returns a list of all keyboards
"""Returns a list of all keyboards.
"""
# We avoid pathlib here because this is performance critical code.
kb_wildcard = os.path.join(base_path, "**", 'keyboard.json')
@@ -184,6 +185,9 @@ def list_keyboards():
found = map(_find_name, paths)
# Convert to posix paths for consistency
found = map(lambda x: str(Path(x).as_posix()), found)
return sorted(set(found))

View File

@@ -62,7 +62,7 @@ uint8_t get_first_key(void) {
/** \brief Checks if a key is pressed in the report
*
* Returns true if the keyboard_report reports that the key is pressed, otherwise false
* Note: The function doesn't support modifiers currently, and it returns false for KC_NO
* Note: The function doesn't support modifers currently, and it returns false for KC_NO
*/
bool is_key_pressed(uint8_t key) {
if (key == KC_NO) {

553
util/env-bootstrap.sh Executable file
View File

@@ -0,0 +1,553 @@
#!/usr/bin/env sh
# Copyright 2025 Nick Brassel (@tzarc)
# SPDX-License-Identifier: GPL-2.0-or-later
################################################################################
# This script will install the QMK CLI, toolchains, and flashing utilities.
################################################################################
# Environment variables:
# CONFIRM: Skip the pre-install delay. (or: --confirm)
# QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...)
# UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...)
# UV_TOOL_DIR: The directory to install `uv` tools to. (or: --uv-tool-dir=...)
# SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean)
# SKIP_PACKAGE_MANAGER: Skip installing the necessary packages for the package manager. (or: --skip-package-manager)
# SKIP_UV: Skip installing `uv`. (or: --skip-uv)
# SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli)
# SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains)
# SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils)
# SKIP_UDEV_RULES: Skip installing the udev rules for Linux. (or: --skip-udev-rules)
# SKIP_WINDOWS_DRIVERS: Skip installing the Windows drivers for the flashing utilities. (or: --skip-windows-drivers)
#
# Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`).
################################################################################
# Usage:
# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh
#
# Help:
# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --help
#
# An example which skips installing `uv` using environment variables:
# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | SKIP_UV=1 sh
#
# ...or by using command line arguments:
# curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --skip-uv
#
# Any other configurable items listed above may be specified in the same way.
################################################################################
{ # this ensures the entire script is downloaded #
set -eu
BOOTSTRAP_TMPDIR="$(mktemp -d /tmp/qmk-bootstrap-failure.XXXXXX)"
trap 'rm -rf "$BOOTSTRAP_TMPDIR" >/dev/null 2>&1 || true' EXIT
FAILURE_FILE="${BOOTSTRAP_TMPDIR}/fail"
# Work out which `sed` to use
command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed
script_args() {
cat <<__EOT__
--help -- Shows this help text
--confirm -- Skips the delay before installation
--uv-install-dir={path} -- The directory to install \`uv\` into
--uv-tool-dir={path} -- The directory to install \`uv\` tools into
--qmk-distrib-dir={path} -- The directory to install the QMK distribution into
--skip-clean -- Skip cleaning the QMK distribution directory
--skip-package-manager -- Skip installing the necessary packages for the package manager
--skip-uv -- Skip installing \`uv\`
--skip-qmk-cli -- Skip installing the QMK CLI
--skip-qmk-toolchains -- Skip installing the QMK toolchains
--skip-qmk-flashutils -- Skip installing the QMK flashing utilities
--skip-udev-rules -- Skip installing the udev rules for Linux
--skip-windows-drivers -- Skip installing the Windows drivers for the flashing utilities
__EOT__
}
signal_execution_failure() {
touch "$FAILURE_FILE" >/dev/null 2>&1 || true
}
exit_if_execution_failed() {
if [ -e "$FAILURE_FILE" ]; then
exit 1
fi
}
script_help() {
echo "$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\s*@@g' -e 's@\s\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\n' ' ')"
echo
echo "Arguments:"
script_args
echo
echo "Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean')."
}
script_parse_args() {
local N
local V
while [ ! -z "${1:-}" ]; do
case "$1" in
--help)
script_help
exit 0
;;
--*=*)
N=${1%%=*}
N=${N##--}
N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')
V=${1##*=}
export $N="$V"
;;
--no-*)
N=${1##--no-}
N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')
unset $N
;;
--*)
N=${1##--}
N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')
export $N=true
;;
*)
echo "Unknown argument: '$1'" >&2
echo
script_help >&2
exit 1
;;
esac
shift
unset N
unset V
done
}
nsudo() {
if [ "$(fn_os)" = "windows" ]; then
# No need for sudo under QMK MSYS
return
elif [ $(id -u) -ne 0 ]; then
echo "sudo"
fi
true
}
download_url() {
local url=$1
local filename=${2:-$(basename "$url")}
local quiet=''
if [ -n "$(command -v curl 2>/dev/null || true)" ]; then
[ "$filename" = "-" ] && quiet='-s' || echo "Downloading '$url' => '$filename'" >&2
curl -LSf $quiet -o "$filename" "$url"
elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then
[ "$filename" = "-" ] && quiet='-q' || echo "Downloading '$url' => '$filename'" >&2
wget $quiet "-O$filename" "$url"
else
echo "Please install 'curl' to continue." >&2
exit 1
fi
}
github_api_call() {
local url="$1"
local token="${GITHUB_TOKEN:-${GH_TOKEN:-}}"
if [ -n "${token:-}" ]; then
if [ -n "$(command -v curl 2>/dev/null || true)" ]; then
curl -fsSL -H "Authorization: token $token" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/$url"
elif [ -n "$(command -v wget 2>/dev/null || true)" ]; then
wget -q --header="Authorization: token $token" --header="Accept: application/vnd.github.v3+json" "https://api.github.com/$url" -O -
fi
else
download_url "https://api.github.com/$url" -
fi
}
fn_os() {
local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z')
if [ -z "$os_name" ]; then
os_name=$(uname -s | tr 'A-Z' 'a-z')
fi
case "$os_name" in
*darwin* | *macos* | *apple*)
echo macos
;;
*windows* | *mingw* | *msys*)
echo windows
;;
*linux*)
echo linux
;;
*)
echo unknown
;;
esac
}
fn_arch() {
local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z')
if [ -z "$arch_name" ]; then
arch_name=$(uname -m | tr 'A-Z' 'a-z')
fi
case "$arch_name" in
*arm64* | *aarch64*)
echo ARM64
;;
*riscv64*)
echo RV64
;;
*x86_64* | *x64*)
echo X64
;;
*)
echo unknown
;;
esac
}
preinstall_delay() {
[ -z "${CONFIRM:-}" ] || return 0
echo >&2
echo "Waiting 10 seconds before proceeding. Press Ctrl+C to cancel installation." >&2
sleep 10
}
get_package_manager_deps() {
case $(fn_os) in
macos) echo "zstd clang-format make hidapi libusb dos2unix git" ;;
windows) echo "base-devel: zstd:p toolchain:p clang:p hidapi:p dos2unix: git: unzip:" ;;
linux)
case $(grep ID /etc/os-release) in
*arch* | *manjaro* | *cachyos*) echo "zstd base-devel clang diffutils wget unzip zip hidapi dos2unix git" ;;
*debian* | *ubuntu*) echo "zstd build-essential clang-format diffutils wget unzip zip libhidapi-hidraw0 dos2unix git" ;;
*fedora*) echo "zstd clang diffutils which gcc git wget unzip zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git epel-release" ;;
*suse*) echo "zstd clang diffutils wget unzip zip libhidapi-hidraw0 dos2unix git libusb-1_0-devel gzip" ;;
*)
echo >&2
echo "Sorry, we don't recognize your distribution." >&2
echo >&2
echo "Proceeding with the installation, however you will need to install at least the following tools manually:" >&2
echo " - make, git, curl, zstd, unzip, [lib]hidapi" >&2
echo "Other tools may be required depending on your distribution." >&2
echo >&2
echo "Alternatively, if you prefer Docker, try using the docker image instead:" >&2
echo " - https://docs.qmk.fm/#/getting_started_docker" >&2
;;
esac
;;
*)
# We can only really support macOS, Windows, and Linux at this time due to `uv` requirements.
echo >&2
echo "Sorry, we don't recognize your OS. Try using a compatible OS instead:" >&2
echo " - https://docs.qmk.fm/newbs_getting_started#set-up-your-environment" >&2
echo >&2
echo "If you cannot use a compatible OS, you can try installing the \`qmk\` Python package manually using \`pip\`, most likely requiring a virtual environment:" >&2
echo " % python3 -m pip install qmk" >&2
echo >&2
echo "All other dependencies will need to be installed manually, such as make, git, AVR and ARM toolchains, and associated flashing utilities." >&2
echo >&2
echo "**NOTE**: QMK does not provide official support for your environment. Here be dragons, you are on your own." >&2
signal_execution_failure
;;
esac
}
print_package_manager_deps_and_delay() {
get_package_manager_deps | tr ' ' '\n' | sort | xargs -I'{}' echo " - {}" >&2
exit_if_execution_failed
preinstall_delay || exit 1
}
install_package_manager_deps() {
# Install the necessary packages for the package manager
case $(fn_os) in
macos)
if [ -n "$(command -v brew 2>/dev/null || true)" ]; then
echo "It will also install the following system packages using 'brew':" >&2
print_package_manager_deps_and_delay
brew update && brew upgrade --formulae
brew install $(get_package_manager_deps)
else
echo "Please install 'brew' to continue. See https://brew.sh/ for more information." >&2
exit 1
fi
;;
windows)
echo "It will also install the following packages using 'pacman'/'pacboy':" >&2
print_package_manager_deps_and_delay
$(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys
$(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps)
;;
linux)
case $(grep ID /etc/os-release) in
*arch* | *manjaro* | *cachyos*)
echo "It will also install the following system packages using 'pacman':" >&2
print_package_manager_deps_and_delay
$(nsudo) pacman --needed --noconfirm -S $(get_package_manager_deps)
;;
*debian* | *ubuntu*)
echo "It will also install the following system packages using 'apt':" >&2
print_package_manager_deps_and_delay
$(nsudo) apt-get update
DEBIAN_FRONTEND=noninteractive \
$(nsudo) apt-get --quiet --yes install $(get_package_manager_deps)
;;
*fedora*)
echo "It will also install the following system packages using 'dnf':" >&2
print_package_manager_deps_and_delay
# Some RHEL-likes need EPEL for hidapi
$(nsudo) dnf -y install epel-release 2>/dev/null || true
# RHEL-likes have some naming differences in libusb packages, so manually handle those
$(nsudo) dnf -y install $(get_package_manager_deps | tr ' ' '\n' | grep -v 'epel-release' | grep -v libusb | tr '\n' ' ')
for pkg in $(get_package_manager_deps | tr ' ' '\n' | grep libusb); do
$(nsudo) dnf -y install "$pkg" 2>/dev/null || true
done
;;
*opensuse* | *suse*)
echo "It will also install development tools as well as the following system packages using 'zypper':" >&2
print_package_manager_deps_and_delay
$(nsudo) zypper --non-interactive refresh
$(nsudo) zypper --non-interactive install -t pattern devel_basis devel_C_C++
$(nsudo) zypper --non-interactive install $(get_package_manager_deps)
;;
*)
print_package_manager_deps_and_delay
echo "Proceeding with the installation, you will need to ensure prerequisites are installed." >&2
;;
esac
;;
*)
print_package_manager_deps_and_delay
;;
esac
}
install_uv() {
# Install `uv` (or update as necessary)
download_url https://astral.sh/uv/install.sh - | TMPDIR="$(windows_ish_path "${TMPDIR:-}")" UV_INSTALL_DIR="$(windows_ish_path "${UV_INSTALL_DIR:-}")" sh
}
setup_paths() {
# Set up the paths for any of the locations `uv` expects
if [ -n "${XDG_BIN_HOME:-}" ]; then
export PATH="$XDG_BIN_HOME:$PATH"
fi
if [ -n "${XDG_DATA_HOME:-}" ]; then
export PATH="$XDG_DATA_HOME/../bin:$PATH"
fi
[ ! -d "$HOME/.local/bin" ] || export PATH="$HOME/.local/bin:$PATH"
if [ -n "${UV_INSTALL_DIR:-}" ]; then
export PATH="$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH" # cater for both "flat" and "hierarchical" installs of `uv`
fi
if [ -n "${UV_TOOL_BIN_DIR:-}" ]; then
export PATH="$UV_TOOL_BIN_DIR:$PATH"
fi
}
uv_command() {
if [ "$(fn_os)" = "windows" ]; then
UV_TOOL_DIR="$(windows_ish_path "${UV_TOOL_DIR:-}")" \
UV_TOOL_BIN_DIR="$(windows_ish_path "${UV_TOOL_BIN_DIR:-}")" \
uv "$@"
else
uv "$@"
fi
}
install_qmk_cli() {
# Install the QMK CLI
uv_command tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk
# QMK is installed to...
local qmk_tooldir="$(posix_ish_path "$(uv_command tool dir)/qmk")"
# Activate the environment
if [ -e "$qmk_tooldir/bin" ]; then
. "$qmk_tooldir/bin/activate"
elif [ -e "$qmk_tooldir/Scripts" ]; then
. "$qmk_tooldir/Scripts/activate"
else
echo "Could not find the QMK environment to activate." >&2
exit 1
fi
# Install the QMK dependencies
uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt
uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt
# Deactivate the environment
deactivate
}
install_toolchains() {
# Get the latest toolchain release from https://github.com/qmk/qmk_toolchains
local latest_toolchains_release=$(github_api_call repos/qmk/qmk_toolchains/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
# Download the specific release asset with a matching keyword
local toolchain_url=$(github_api_call repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch))
if [ -z "$toolchain_url" ]; then
echo "No toolchain found for this OS/Arch combination." >&2
exit 1
fi
# Download the toolchain release to the toolchains location
echo "Downloading compiler toolchains..." >&2
local target_file="$QMK_DISTRIB_DIR/$(basename "$toolchain_url")"
download_url "$toolchain_url" "$target_file"
# Extract the toolchain
echo "Extracting compiler toolchains to '$QMK_DISTRIB_DIR'..." >&2
zstdcat "$target_file" | tar xf - -C "$QMK_DISTRIB_DIR" --strip-components=1
}
install_flashing_tools() {
# Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils
local latest_flashutils_release=$(github_api_call repos/qmk/qmk_flashutils/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
# Download the specific release asset with a matching keyword
local flashutils_url=$(github_api_call repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep $(fn_os)$(fn_arch))
if [ -z "$flashutils_url" ]; then
echo "No flashing tools found for this OS/Arch combination." >&2
exit 1
fi
# Download the flashing tools release to the toolchains location
echo "Downloading flashing tools..." >&2
local target_file="$QMK_DISTRIB_DIR/$(basename "$flashutils_url")"
download_url "$flashutils_url" "$target_file"
# Extract the flashing tools
echo "Extracting flashing tools to '$QMK_DISTRIB_DIR'..." >&2
zstdcat "$target_file" | tar xf - -C "$QMK_DISTRIB_DIR/bin"
# Move the release file to etc
mv "$QMK_DISTRIB_DIR/bin/flashutils_release"* "$QMK_DISTRIB_DIR/etc"
}
install_linux_udev_rules() {
# Download the udev rules to the toolchains location
echo "Downloading QMK udev rules file..." >&2
local qmk_rules_target_file="$QMK_DISTRIB_DIR/50-qmk.rules"
download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules" "$qmk_rules_target_file"
# Install the udev rules -- path list is aligned with qmk doctor's linux.py
local udev_rules_paths="
/usr/lib/udev/rules.d
/usr/local/lib/udev/rules.d
/run/udev/rules.d
/etc/udev/rules.d
"
for udev_rules_dir in $udev_rules_paths; do
if [ -d "$udev_rules_dir" ]; then
echo "Installing udev rules to $udev_rules_dir/50-qmk.rules ..." >&2
$(nsudo) mv "$qmk_rules_target_file" "$udev_rules_dir"
$(nsudo) chown 0:0 "$udev_rules_dir/50-qmk.rules"
$(nsudo) chmod 644 "$udev_rules_dir/50-qmk.rules"
break
fi
done
# Reload udev rules
if command -v udevadm >/dev/null 2>&1; then
echo "Reloading udev rules..." >&2
$(nsudo) udevadm control --reload-rules || true
$(nsudo) udevadm trigger || true
else
echo "udevadm not found, skipping udev rules reload." >&2
fi
}
install_windows_drivers() {
# Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer
local latest_driver_installer_release=$(github_api_call repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '"tag_name": "[^"]+' | grep -oE '[^"]+$')
# Download the specific release asset
local driver_installer_url=$(github_api_call repos/qmk/qmk_driver_installer/releases/tags/$latest_driver_installer_release - | grep -oE '"browser_download_url": "[^"]+"' | grep -oE 'https://[^"]+' | grep '\.exe')
if [ -z "$driver_installer_url" ]; then
echo "No driver installer found." >&2
exit 1
fi
# Download the driver installer release to the toolchains location
echo "Downloading driver installer..." >&2
local target_file="$QMK_DISTRIB_DIR/$(basename "$driver_installer_url")"
download_url "$driver_installer_url" "$target_file"
# Download the drivers list
download_url "https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/drivers.txt" "$QMK_DISTRIB_DIR/drivers.txt"
# Execute the driver installer
cd "$QMK_DISTRIB_DIR"
cmd.exe //c "qmk_driver_installer.exe --all --force drivers.txt"
cd -
# Remove the temporary files
rm -f "$QMK_DISTRIB_DIR/qmk_driver_installer.exe" "$QMK_DISTRIB_DIR/drivers.txt" || true
}
clean_tarballs() {
# Clean up the tarballs
rm -f "$QMK_DISTRIB_DIR"/*.tar.zst || true
}
windows_ish_path() {
[ -n "$1" ] || return 0
[ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -w "$1" || echo "$1"
}
posix_ish_path() {
[ -n "$1" ] || return 0
[ "$(uname -o 2>/dev/null || true)" = "Msys" ] && cygpath -u "$1" || echo "$1"
}
# Set the Python version we want to use with the QMK CLI
export PYTHON_TARGET_VERSION=3.13
# Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory.
# Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS.
if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then
export TMPDIR="$(posix_ish_path "$TMP")"
export UV_INSTALL_DIR="$(posix_ish_path "${UV_INSTALL_DIR:-/opt/uv}")"
export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-/opt/qmk}")"
export UV_TOOL_DIR="$(posix_ish_path "${UV_TOOL_DIR:-"$UV_INSTALL_DIR/tools"}")"
export UV_TOOL_BIN_DIR="$(posix_ish_path "$UV_TOOL_DIR/bin")"
fi
script_parse_args "$@"
echo "This QMK CLI installation script will install \`uv\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities." >&2
[ -z "${SKIP_PACKAGE_MANAGER:-}" ] || { preinstall_delay || exit 1; }
[ -n "${SKIP_PACKAGE_MANAGER:-}" ] || install_package_manager_deps
[ -n "${SKIP_UV:-}" ] || install_uv
# Make sure the usual `uv` and other associated directories are on the $PATH
setup_paths
# Work out where we want to install the distribution and tools now that `uv` is installed
export QMK_DISTRIB_DIR="$(posix_ish_path "${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\nprint(platformdirs.user_data_dir("qmk"))' | uv_command run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)}")"
# Clear out the distrib directory if necessary
if [ -z "${SKIP_CLEAN:-}" ] || [ -z "${SKIP_QMK_TOOLCHAINS:-}" -a -z "${SKIP_QMK_FLASHUTILS:-}" ]; then
if [ -d "$QMK_DISTRIB_DIR" ]; then
echo "Removing old QMK distribution..." >&2
rm -rf "$QMK_DISTRIB_DIR"
fi
fi
mkdir -p "$QMK_DISTRIB_DIR"
[ -n "${SKIP_QMK_CLI:-}" ] || install_qmk_cli
[ -n "${SKIP_QMK_TOOLCHAINS:-}" ] || install_toolchains
[ -n "${SKIP_QMK_FLASHUTILS:-}" ] || install_flashing_tools
if [ "$(uname -s 2>/dev/null || true)" = "Linux" ]; then
[ -n "${SKIP_UDEV_RULES:-}" ] || install_linux_udev_rules
fi
if [ "$(uname -o 2>/dev/null || true)" = "Msys" ]; then
[ -n "${SKIP_WINDOWS_DRIVERS:-}" ] || install_windows_drivers
fi
clean_tarballs
# Notify the user that they may need to restart their shell to get the `qmk` command
echo >&2
echo "QMK CLI installation complete." >&2
echo "The QMK CLI has been installed to '$(posix_ish_path "$(dirname "$(command -v qmk)")")'." >&2
echo "The QMK CLI venv has been created at '$(posix_ish_path "$(uv_command tool dir)/qmk")'." >&2
echo "Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'." >&2
echo >&2
echo "You may need to restart your shell to gain access to the 'qmk' command." >&2
echo "Alternatively, add "$(posix_ish_path "$(dirname "$(command -v qmk)")")" to your \$PATH:" >&2
echo " export PATH=\"$(posix_ish_path "$(dirname "$(command -v qmk)")"):\$PATH\"" >&2
} # this ensures the entire script is downloaded #