Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4ca5293
feat(rmk-types): replace rmk protocol module with rynk wire format
HaoboGu May 11, 2026
514cffe
refactor: clean a little bit
HaoboGu May 13, 2026
e5e9870
refactor: format code
HaoboGu May 13, 2026
2bb7d91
feat(rmk-types): add topic-snapshot Get cmds
HaoboGu May 13, 2026
de74af1
refactor(rmk-types): replace Header with Frame + FrameOps
HaoboGu May 13, 2026
e51180e
feat(rmk-types): add RynkResult<T> alias for handler responses
HaoboGu May 13, 2026
f771869
feat(rmk): add rynk host service core (codec, topics, context wiring)
HaoboGu May 11, 2026
ce38153
refactor(rmk): drop RYNK_RX_CHANNEL and BLE_RYNK_READY from pr2
HaoboGu May 13, 2026
1b28f84
feat(rmk): cache topic snapshots for host polling
HaoboGu May 13, 2026
14adb15
refactor(rmk): rewrite dispatch on FrameOps, single buffer
HaoboGu May 13, 2026
99f5be8
refactor(rynk): rename frame to message, refactor RynkError
HaoboGu May 14, 2026
d4e8613
refactor(rynk): RynkMessage methods validate length, return Result
HaoboGu May 14, 2026
fadf19e
refactor(rynk): dispatch propagates handler errors via Result
HaoboGu May 14, 2026
b72c48f
feat(rmk): add rynk command handlers
HaoboGu May 14, 2026
8009a08
refactor: make `RynkMessage` a wrapper type
HaoboGu May 24, 2026
32c0853
feat(rmk): add USB and BLE rynk transports plus split glue
HaoboGu May 11, 2026
de54842
refactor(rmk): redesign transport, use serial
HaoboGu May 13, 2026
212353c
refactor: splitting RynkError::InvalidRequest into Malformed + Invali…
HaoboGu May 26, 2026
6525df4
refactor(rynk): use pipe for receiving data from BLE
HaoboGu May 28, 2026
dcc99ff
fix(rynk): process 1 frame per run in
HaoboGu May 29, 2026
ef4ba75
test(rmk): add rynk dispatch loopback integration test
HaoboGu Jun 3, 2026
a1df6c2
feat(rynk): add host-side client implementation
HaoboGu Jun 11, 2026
f65fb78
refactor: update examples to use vial
HaoboGu Jun 24, 2026
9c68fb1
feat(rmk): add RynkHidService — rynk config over BLE WebHID
HaoboGu Jun 29, 2026
075c64a
refactor(rmk-types): add tsify wasm/TS type generation
HaoboGu Jun 29, 2026
6b5355c
examples: add qemu-riscv-rynk
liyang8246 Jun 10, 2026
32cae31
feat: test more Rynk behaviors in qemu
HaoboGu Jun 29, 2026
9359a03
feat(rynk-wasm): wasm bridge + WebHID transport for the web demo
HaoboGu Jun 29, 2026
cd1f5b9
feat: cancel-safe implementation of wasm session
HaoboGu Jun 30, 2026
1234cc1
refactor: remove pump and Aborthandle
HaoboGu Jul 1, 2026
b4c976a
feat: redesign the RynkDevice trait, impl it for rynk-wasm
HaoboGu Jul 1, 2026
8a553ca
feat: simplify error and bitfield types
HaoboGu Jul 1, 2026
2c62154
feat(wasm): generate typescript types
HaoboGu Jul 1, 2026
1ff9357
refactor: clean files
HaoboGu Jul 1, 2026
d6c4b19
docs: rynk feature docs + v0.9 migration guide
HaoboGu Jul 2, 2026
cd2b6eb
docs: stop hard-wrapping .mdx
HaoboGu Jul 2, 2026
8ec5744
docs: fix format
HaoboGu Jul 2, 2026
c27ae65
docs: align v0.9 migration guide with merged main (drop flow-tap-glob…
HaoboGu Jul 2, 2026
548b5b3
docs: freshen READMEs + docs for v0.9 (rynk default; fix rynk/wasm AP…
HaoboGu Jul 2, 2026
964a389
test(rynk): regenerate wire snapshot for MorseProfile bit-repack
HaoboGu Jul 6, 2026
f69a314
feat(rynk): add GetDeviceInfo endpoint (0x000A)
HaoboGu Jul 6, 2026
ccc3951
docs(rynk): add lock-gate security design
HaoboGu Jul 6, 2026
873497c
feat: implement lock feature.
HaoboGu Jul 7, 2026
899b695
feat: add rynk layout definition in keyboard.toml
HaoboGu Jun 30, 2026
c382366
refactor(examples): migrate configs to [layout].map + [[keymap.layer]]
HaoboGu Jul 2, 2026
27eaf2e
refactor(rmk-config): drop legacy keymap path and fix five design issues
HaoboGu Jul 2, 2026
b6b7f6e
feat: add layout blob
HaoboGu Jul 2, 2026
a95ac4b
feat(examples): add rp2040_rynk use_config example
HaoboGu Jul 2, 2026
6ef0396
feat: add KLE conversion
HaoboGu Jul 3, 2026
ccccedc
refactor: clean code, update simplify docs
HaoboGu Jul 3, 2026
6c5f28b
feat(rynk): implement bulk transfer endpoints
HaoboGu Jul 6, 2026
42f4b94
fix(rynk): let keymap bulk transfer span layer boundaries
HaoboGu Jul 6, 2026
dd75961
feat(rynk): decouple keymap bulk size from combo/morse bulk size
HaoboGu Jul 7, 2026
9b92e2b
refactor: calculate bulk sizes from buffer size
HaoboGu Jul 9, 2026
9f0eaf8
fix: various CI related fix
HaoboGu Jul 9, 2026
decfbbb
refactor: various simplify of code and comments
HaoboGu Jul 9, 2026
87fb488
refactor: introduce `HostGattHandler` for both vial and rynk
HaoboGu Jul 10, 2026
62a9432
refactor: clean code
HaoboGu Jul 10, 2026
e7bee85
chore: update `Cargo.lock`s
HaoboGu Jul 11, 2026
da64557
refactor: improve the endpoints macro
HaoboGu Jul 11, 2026
20d2f95
fix: align docs.rs features with rynk
HaoboGu Jul 11, 2026
ffe456b
fix: expose rynk layout API to wasm
HaoboGu Jul 11, 2026
fde0720
Merge pull request #919 from HaoboGu/fix/pr848-docs-rs
HaoboGu Jul 11, 2026
37a5b8a
Merge pull request #920 from HaoboGu/fix/pr848-wasm-layout
HaoboGu Jul 11, 2026
8a43fc5
fix: model rynk macros as a flat buffer
HaoboGu Jul 11, 2026
7ae8908
Merge pull request #921 from HaoboGu/fix/pr848-macro-api
HaoboGu Jul 11, 2026
4ac600a
fix: validate rynk capability bounds
HaoboGu Jul 11, 2026
fbefe12
refactor: enforce capability bounds once, at build time
HaoboGu Jul 11, 2026
ba955d2
Merge pull request #922 from HaoboGu/fix/pr848-capability-bounds
HaoboGu Jul 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 11 additions & 4 deletions .github/ci/_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,25 @@ log_section() {
printf "\n==> %s\n" "$1"
}

# Feature-set matrix for rmk check/clippy/test. An empty entry means
# `--no-default-features` with no extra features on top. Kept here so
# check.sh and test.sh stay in lockstep — a set added for check is also
# exercised by tests, and vice versa.
# Shared rmk feature-set matrix; empty means only `--no-default-features`.
RMK_FEATURESETS=(
""
"log,std"
"storage"
"async_matrix,storage"
"vial,storage"
"vial,_ble"
"split,async_matrix"
"split,async_matrix,_ble"
"split,vial,async_matrix"
"split,vial,async_matrix,_ble"
"split,vial,storage"
"passkey_entry"
"split,vial,storage,passkey_entry"
"vial,storage,steno"
"split,vial,storage,async_matrix,_ble,steno"
"rynk,bulk,_ble,split,storage,async_matrix"
"rynk,storage"
)

# Examples auto-discovery skiplist. Reasons:
Expand Down
3 changes: 3 additions & 0 deletions .github/ci/format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ for crate in rmk rmk-config rmk-macro rmk-types; do
cargo +nightly fmt --manifest-path "$crate/Cargo.toml" --check
done

log_section "Formatting host tooling"
cargo +nightly fmt --manifest-path rynk/Cargo.toml --all --check

log_section "Formatting examples"
while IFS= read -r manifest; do
cargo +nightly fmt --manifest-path "$manifest" --check
Expand Down
30 changes: 30 additions & 0 deletions .github/ci/host.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
set -euo pipefail
# shellcheck source=_lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh"

# The host tooling is its own cargo workspace.
cd "$repo_root/rynk"

export CARGO_TERM_COLOR=always
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$target_root/host}"
mkdir -p "$CARGO_TARGET_DIR"

log_section "Tests"
cargo +stable test --workspace --lib --tests

log_section "Doctests"
cargo +stable test -p rynk --doc

log_section "Wasm smoke check"
cargo +stable check -p rynk --lib --target wasm32-unknown-unknown
cargo +stable check -p rynk-wasm --target wasm32-unknown-unknown

log_section "Wasm package build"
# wasm-pack emits the JS package plus generated .d.ts files under rynk-wasm/pkg/.
# The generated package is intentionally ignored rather than checked in.
(cd rynk-wasm && wasm-pack build --target web >/dev/null)

log_section "Clippy"
cargo +stable clippy --workspace --lib --tests --examples -- -D warnings
cargo +stable clippy -p rynk-wasm --target wasm32-unknown-unknown -- -D warnings
5 changes: 2 additions & 3 deletions .github/ci/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ nx=(nextest run --config-file "$nextest_cfg" --profile ci)
log_section "Running tests"
cargo +stable "${nx[@]}" --manifest-path rmk-config/Cargo.toml
cargo +stable "${nx[@]}" --manifest-path rmk-types/Cargo.toml
# Exercise the rmk_protocol module (gated behind `rmk_protocol`) so the wire-format
# snapshot tests under rmk-types/src/protocol/rmk/snapshots/ run in CI. `host`
# enables rmk_protocol + bulk + _ble + split, covering every snapshot.
# Exercise the rynk protocol module (gated behind `rynk`).
cargo +stable "${nx[@]}" --manifest-path rmk-types/Cargo.toml --features host
cargo +stable "${nx[@]}" --manifest-path rmk-types/Cargo.toml --features steno
cargo +stable "${nx[@]}" --manifest-path rmk-macro/Cargo.toml
for feats in "${RMK_FEATURESETS[@]}"; do
if [[ -z "$feats" ]]; then
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,49 @@ jobs:
run: cargo install --locked cargo-expand
- run: .github/ci/test.sh

# Host tooling lives in its own workspace, so it gets its own job.
host:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
targets: wasm32-unknown-unknown
- name: Install host system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config
# host.sh runs wasm-pack to verify JS/.d.ts package generation.
- uses: taiki-e/install-action@v2
with:
tool: wasm-pack
- uses: Swatinem/rust-cache@v2
with:
shared-key: ci-host
cache-directories: |
target/ci
- run: .github/ci/host.sh

qemu-rynk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: riscv32imac-unknown-none-elf
- uses: Swatinem/rust-cache@v2
with:
shared-key: ci-qemu-rynk
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-misc libdbus-1-dev pkg-config
qemu-system-riscv32 --version
- name: Run QEMU Rynk behavior verifier
working-directory: examples/use_rust/qemu-riscv-rynk
run: python3 run.py

# Discover buildable examples for the matrix
discover:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
target
node_modules

# wasm-pack output
pkg

.DS_Store

*.bin
Expand All @@ -15,6 +18,7 @@ rmk/Cargo.lock
rmk-macro/Cargo.lock
rmk-config/Cargo.lock
rmk-types/Cargo.lock
rynk/Cargo.lock

# esp idf build files
.embuild
Expand Down
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"rust-analyzer.linkedProjects": [
"rmk-macro/Cargo.toml",
"rmk/Cargo.toml",
"rynk/Cargo.toml",
// To work on the examples, comment the line above and all of the cargo.features lines,
// then uncomment ONE line below to select the chip you want to work on.
// This makes rust-analyzer work on the example crate and all its dependencies.
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,18 @@ Matrix / InputDevices → Events (pub/sub channels) -> InputProcessors/Keyboard(

### `keyboard.toml` and compile-time constants

`keyboard.toml` is parsed by `rmk-config` (`KeyboardTomlConfig`) at two points: by `rmk/build.rs` at build time, and by `rmk-macro` at macro-expansion time. The path defaults to `keyboard.toml` next to `Cargo.toml` and can be overridden with `KEYBOARD_TOML_PATH` in user space's `.cargo/config.toml`.
`keyboard.toml` is parsed by `rmk-config` (`KeyboardTomlConfig`) at two points: by `rmk-types/build.rs` at build time, and by `rmk-macro` at macro-expansion time. The path defaults to `keyboard.toml` next to `Cargo.toml` and can be overridden with `KEYBOARD_TOML_PATH` in user space's `.cargo/config.toml`.

Config is loaded in three layers (later overrides earlier): `event_default.toml` → chip-specific default (from `rmk-config/src/default_config/<chip>.toml`, selected via `[keyboard].chip`) → user `keyboard.toml`.

`build.rs` reads only the `[rmk]` and `[event]` sections, then emits `constants.rs` as Rust `const` items. The full `KeyboardTomlConfig` struct in `rmk-config/src/lib.rs` is the authoritative reference for all available fields and their defaults.
`rmk-types/build.rs` reads only the `[rmk]` and `[event]` sections, then emits `constants.rs` as Rust `const` items. The full `KeyboardTomlConfig` struct in `rmk-config/src/lib.rs` is the authoritative reference for all available fields and their defaults.

`[event]` tunes per-event pub/sub channel sizes (`channel_size`, `pubs`, `subs`). All event names and their defaults live in `rmk-config/src/default_config/event_default.toml`.

## Rules

- Don't use `pub use` for convenient usage **within** the crate
- Don't add a small helper function (≤ 10 lines) that has only one call site — inline it at the call site
- When writing code comments, follow the principles in [Best practices for writing code comments](https://stackoverflow.blog/2021/12/23/best-practices-for-writing-code-comments/)
- When writing documentation and READMEs, follow the principles in [Best practices for GitHub Docs](https://docs.github.com/en/contributing/writing-for-github-docs/best-practices-for-github-docs)
- If you need a paragraph-long comment to justify why the workaround is OK, the code is wrong — fix the code.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
## Features

- **Broad microcontroller compatibility**: Leveraging [embassy](https://github.com/embassy-rs/embassy), RMK supports a comprehensive range of microcontrollers, including stm32, nRF, rp2040(w), esp32, etc
- **Dynamic keymap customization**: RMK offers native [Vial](https://get.vial.today) support, enabling real-time keymap modifications. You can even edit keymaps over BLE connections wirelessly
- **Dynamic keymap customization**: RMK offers real-time keymap modifications through [Rynk](https://rmk.rs/docs/features/rynk), its native protocol enabled by default, or [Vial](https://get.vial.today). You can even edit keymaps over BLE connections wirelessly
- **Advanced keyboard functionality**: RMK comes with lots of advanced keyboard features by default, including layer switching, media controls, system commands, mouse control, and more
- **Wireless connectivity**: BLE wireless support with automatic reconnection and multi-device capabilities for nRF52 and esp32 microcontrollers, tested on nRF52840, esp32c3, esp32s3, Pi Pico W
- **Easy configuration**: RMK simplifies keyboard development through a single `keyboard.toml` configuration file. For Rust enthusiasts, the firmware remains highly customizable using Rust code
Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
## 特性

- **MCU支持丰富**:基于 [embassy](https://github.com/embassy-rs/embassy),RMK 支持非常多的MCU系列,例如 stm32/nrf/rp2040/esp32等。
- **实时键位编辑**:RMK 默认支持 Vial 进行实时键位编辑,即时生效。您甚至可以通过 BLE 使用 Vial 来编辑键位
- **实时键位编辑**:RMK 通过其原生协议 [Rynk](https://rmk.rs/docs/features/rynk)(默认启用)或 [Vial](https://get.vial.today) 支持实时键位编辑,即时生效。您甚至可以通过 BLE 无线编辑键位
- **高级键盘功能**:RMK 默认提供许多高级键盘功能,如层切换、媒体控制、系统控制、鼠标控制等。
- **无线支持**:RMK 支持 BLE 无线连接,包括自动重连和多设备支持,已经在 nrf52840, esp32 和 Pi Pico W 上进行了测试。
- **易于配置**:RMK提供了一个非常简单的配置键盘的方法,你只需要一个`keyboard.toml`文件,就可以构建起你的键盘固件(不需要写任何Rust代码)!当然,对于 Rust 开发者来说,你仍然可以使用代码方式来使用 RMK 从而获得更大的灵活性。
Expand Down
50 changes: 32 additions & 18 deletions docs/docs/main/docs/configuration/appendix.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ direct_pin_low_active = true
rows = 5
# Number of cols. For a split keyboard, this is the total number of cols for all splits
cols = 4
# Number of layers. Be careful, since large layer number takes more flash and RAM
layers = 3
# keypad example: (for the key in position (2,1) the `H1` profile is activated)
# keypad example
# ┌───┬───┬───┬───┐
# │NUM│ / │ * │ - │ <-- row 0, col 0..4
# ├───┼───┼───┼───┤
Expand All @@ -63,14 +61,18 @@ layers = 3
# ├───┴───┼───┤ N │
# │ 0 │ . │ T │
# └───────┴───┴───┘
matrix_map = """
(0,0,R) (0,1,R) (0,2,R) (0,3,R)
(1,0,R) (1,1,R) (1,2,R) (1,3,R)
(2,0,R) (2,1,R:H1) (2,2,R)
(3,0,R) (3,1,R) (3,2,R) (3,3,R)
(4,0,R) (4,1,R)
map = """
(0,0,R) (0,1,R) (0,2,R) (0,3,R)
(1,0,R) (1,1,R) (1,2,R) (1,3,R)
(2,0,R) (2,1,R) (2,2,R)
(3,0,R) (3,1,R) (3,2,R) (3,3,R)
(4,0,R) (4,1,R)
"""

[keymap]
# Number of layers. Be careful, since large layer number takes more flash and RAM
layers = 3

# here are the aliases for the example layer.keys below
[aliases]
MyCut = "WM(X, LCtrl)"
Expand All @@ -79,13 +81,13 @@ MyPaste = "WM(V, LCtrl)"

# Key map definitions per layer:
# The number (and order) of entries on each layer should be
# identical with the number (and order) of entries in `matrix_map`.
# identical with the number (and order) of entries in `layout.map`.
# Empty layers will be used to fill if the number of explicitly
# defined layers is smaller than the `layout.layers` setting
# defined layers is smaller than the `keymap.layers` setting

# layer 0 (default):
# (the number comes from the order of '[[layer]] entries' in the file)
[[layer]]
# (the number comes from the order of '[[keymap.layer]] entries' in the file)
[[keymap.layer]]
name = "base_layer" #optional name for the layer
keys = """
NumLock KpSlash KpAsterisk KpMinus
Expand All @@ -96,7 +98,7 @@ Kp1 Kp2 Kp3 Enter
"""

# layer 1:
[[layer]]
[[keymap.layer]]
name = "mouse_navigation" #optional name for the layer
keys = """
TO(base_layer) @MyCut @MyCopy @MyPaste
Expand Down Expand Up @@ -131,7 +133,7 @@ one_shot_modifiers = {
enable_flow_tap = true
prior_idle_time = "120ms"
hold_on_other_press = true
unilateral_false = false
unilateral_tap = false
hold_timeout = "250ms"
gap_timeout = "250ms"

Expand Down Expand Up @@ -185,7 +187,8 @@ morses = [
]

[behavior.morse.profiles]
# matrix_map may refer these to override the defaults given in [behavior.morse] for some key positions - this example is a home row mod
# Named overrides of the [behavior.morse] defaults, referenced by name from a morse key
# (profile = "...") or a tap-hold action's optional profile arg — e.g. a home row mod is MT(key, mod, H1).
H1 = { permissive_hold = true, unilateral_tap = true, hold_timeout = "250ms", gap_timeout = "250ms" }
H2 = { enable_flow_tap = false, permissive_hold = true, unilateral_tap = true, hold_timeout = "200ms", gap_timeout = "200ms" }
MRZ = { normal_mode = true, unilateral_tap = false, hold_timeout = "200ms", gap_timeout = "200ms" }
Expand Down Expand Up @@ -295,6 +298,10 @@ split_peripherals_num = 0
ble_profiles_num = 3
# BLE Split Central sleep timeout in seconds (0 = disabled)
split_central_sleep_timeout_seconds = 0
# Maximum macro data bytes in one Rynk macro request or response
protocol_macro_chunk_size = 64
# Rynk RX/TX buffer size in bytes.
# rynk_buffer_size = 512

# Split configuration
# This section conflicts with the [matrix] section. You can only have either [matrix] or [split], but NOT BOTH
Expand Down Expand Up @@ -395,11 +402,18 @@ defmt_log = true

# Host-side tools configuration
[host]
# Whether Vial is enabled (default: true)
# Whether Vial is enabled (default: true in keyboard.toml config)
vial_enabled = true
# Whether Rynk is enabled (default: false in keyboard.toml config)
# Rynk and Vial are mutually exclusive and must match Cargo features.
rynk_enabled = false
# The unlock keys are the combo of the row 0, col 0 key and
# the row 0, col 1 key
# the row 0, col 1 key. Shared by the Vial lock and the Rynk lock gate.
unlock_keys = [[0, 0], [0, 1]]
# Start (and stay) unlocked. Renamed from `vial_insecure` (still parses).
insecure = false
# Rynk only: move config writes into the locked tier (default: false).
write_requires_unlock = false

# Chip-specific configuration
# To use the default configuration, ignore this section completely
Expand Down
Loading
Loading