Skip to content

keychron: harden CKBT51 Bluetooth link and raise report rate to 133 Hz (26 Pro models) - #529

Open
thementalcoding wants to merge 6 commits into
Keychron:wls_2025q1from
thementalcoding:k13-pro-bt-fixes
Open

thementalcoding wants to merge 6 commits into
Keychron:wls_2025q1from
thementalcoding:k13-pro-bt-fixes

Conversation

@thementalcoding

@thementalcoding thementalcoding commented Oct 26, 2026 •

Copy link
Copy Markdown

keychron: harden CKBT51 Bluetooth link and raise report rate to 133 Hz (26 Pro models)

Summary

Six commits improving Bluetooth reliability and report rate on the Keychron
CKBT51-based Pro range (K Pro / Q Pro):

  1. bluetooth/ — four link-handling fixes: bound the UART frame parser and
    chunk the DFU TX path; flush the pending report queue on reconnection; reset
    the retry timer on ACK_FIFO_FULL_ERROR instead of resending immediately;
    and gate STOP1 so low-power mode is not entered with work still pending.
    Connect attempts are also rate-limited (one per 500 ms), and an opt-in
    CKBT51_FAST_WAKE is compiled out by default.
  2. Module parameters on 26 boards: report_rate 90 → 133 Hz and
    reconnect_timeout 5 → 60 s, applied on the first Bluetooth init
    after power-on. The old 1 s (and k13_pro's 5 s) MCU-uptime window is gone, so
    a slow module boot or a late switch to Bluetooth no longer leaves the module
    on factory defaults.
  3. k13_pro/: LPM tunables documented in config.h; the debounce
    experiment is recorded as opt-in only (not enabled).
  4. q6_pro/: removed an accidental ckbt51_factory_reset() bound to
    Fn+F3 that wiped pairing and module parameters on every keymap.
  5. q1_pro/: the advertised Bluetooth name no longer includes literal
    quotes ("Keychron Q1 Pro" → Keychron Q1 Pro).

Scope: 26 CKBT51 boards, 95 variants (K1–K17 Pro, Q1–Q14 Pro), plus the
q6_pro/q1_pro point fixes. LKBT51 (the separate SPI stack) is intentionally
untouched. No JSON/PID/layout changes; default behaviour is unchanged except
for the parameters above and the Fn+F3 fix.

Why

A K13 Pro (ISO RGB, Linux host, Intel AX210) intermittently produced bursts of
repeated keystrokes and "silent" reconnections after short idle periods.
Analysis of the QMK side found:

  • If EVT_DISCONNECTED was lost (UART overrun after STOP1 wake), the report
    queue was never flushed, so the backlog was replayed at ~3 ms/report after
    the link came back — the most likely source of the burst symptom.
  • ckbt51_task() copied up to 255 bytes into a 32-byte stack buffer with no
    length check (a 64-byte GET_MODULE_INFO response is valid), i.e. an
    unbounded UART parser over a fixed buffer.
  • Retries after ACK_FIFO_FULL_ERROR were issued immediately, without backoff.
  • lpm_task() allowed STOP1 with reports, retries or events still pending.
  • Every keystroke while disconnected issued a blocking connect command,
    causing reconnect churn.
  • Module parameters were only applied if the module booted within 1 s of MCU
    start, so slow-booting modules stayed on report_rate = 90 and
    reconnect_timeout = 5 s.

Scope

  • keyboards/keychron/bluetooth/: bluetooth.c, ckbt51.c, lpm.c,
    report_buffer.c, report_buffer.h.
  • keyboards/keychron/*_pro/*_pro.c: all 26 CKBT51 boards (K1–K17 Pro,
    Q1–Q14 Pro), each applying the module parameters on first Bluetooth init.
  • keyboards/keychron/k13_pro/{config.h,rules.mk}: documented LPM tunables;
    opt-in debounce note.
  • keyboards/keychron/q6_pro/q6_pro.c and keyboards/keychron/q1_pro/q1_pro.c:
    the two point fixes.

33 files, all under keyboards/keychron/. No files outside that tree, and no
changes to the LKBT51 stack.

Build evidence

  • 95/95 variants of the 26 boards compile with the QMK CLI and
    arm-gcc 14.2; each image passed DFU-suffix, VID/PID, size, CRC and
    manifest/checksum checks.
  • The 4 K13 Pro variants (ansi/iso, rgb/white) are reproducible
    locally; flash usage ≈ 24 % of 256 KB.
  • Public builds: https://github.com/thementalcoding/keychron-pro-firmware/releases/tag/v1.0.0
    (26 models, 95 images, manifest.json + SHA256SUMS).

Testing

  • Hardware-tested on one unit only: K13 Pro ISO RGB, Linux 6.12 / Intel
    AX210. Minimum inter-report gap 7 → 5 ms, estimated ceiling ~143 → ~200
    reports/s.
  • The burst symptom is not yet confirmed fixed long-term; it needs days of
    field use.
  • All other models are compile-tested only — none of the other 94 images
    has been flashed on hardware.
  • The CKBT51 module firmware is closed and untouched; only the STM32 side is
    modified.

Note on module parameters

The parameters are sent on the first Bluetooth init after power-on, not on
every reconnection. A module that is already initialised keeps its values until
the keyboard is power-cycled.

Checklist

  • qmk compile for all 26 boards / 95 variants (QMK CLI, arm-gcc 14.2)
  • clang-format 19.1.7 --dry-run --Werror clean on changed files
  • qmk lint on changed keyboards: only pre-existing findings in this
    branch (empty variant rules.mk, via keymap header, license header),
    none introduced here
  • Changes limited to keyboards/keychron/**
  • Hardware validation on the remaining models

…08/201)

- UART parser: bound frame length to PACKET_MAX_LEN, chunked dfu_tx, resync
- flush report queue on EVT_RECONNECTING; retry timer reset on FIFO_FULL
- drop dead bluetooth_report_protocol; LPM gate: no STOP1 with pending reports/retry/events
- rate-limit bluetooth_connect() to 1/500ms; opt-in CKBT51_FAST_WAKE (off by default)
…P-110/111a)

Applies uniformly to all CKBT51 boards; LKBT51 stack intentionally untouched.
25 boards only applied report_rate/reconnect_timeout if the CKBT51 module
booted within 1s of MCU start; a slow module boot or switching to Bluetooth
later left the module running on factory defaults. Use the existing
firstDisconnect flag instead of an uptime window, and align k13_pro.
The KC_TASK case in q6_pro's process_record_kb() called
ckbt51_factory_reset() on key press, so Fn+F3 wiped the Bluetooth
pairing information and module parameters on every q6_pro keymap
(default and via, ansi and iso encoders). None of the other Keychron
models that handle KC_TASK do this. Drop the stray call so Fn+F3 only
emits the task-view shortcut like the rest of the family.
ckbt51_set_local_name() expects a plain C string, but q1_pro passed
STR(PRODUCT), so the advertised name included the literal quotes
("Keychron Q1 Pro"). Use PRODUCT directly in both the own_bt_param_init()
and ckbt51_param_init() paths, matching every other CKBT51 board.
@thementalcoding thementalcoding changed the title keychron: CKBT51 link hardening + 133 Hz report rate keychron: harden CKBT51 Bluetooth link and raise report rate to 133 Hz (26 Pro models) Oct 26, 2026
@thementalcoding

Copy link
Copy Markdown
Author

Added two commits on top of the CKBT51 work:

  • keychron: remove accidental CKBT51 factory reset from q6_pro Fn+F3 — Fn+F3 no longer wipes Bluetooth pairing/module parameters.
  • keychron: fix quoted Bluetooth name on q1_pro — the advertised name is published without the literal quotes.

The PR now covers 26 CKBT51 boards / 95 variants, with the two point fixes above.
Public builds and checksums for all 95 images: https://github.com/thementalcoding/keychron-pro-firmware/releases/tag/v1.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant