Skip to content

[Knock to trigger macros] Beat your printer into obedience! - #403

Open
shawn-makes-stuff wants to merge 1 commit into
OpenCentauri:mainfrom
shawn-makes-stuff:knock-gestures
Open

shawn-makes-stuff wants to merge 1 commit into
OpenCentauri:mainfrom
shawn-makes-stuff:knock-gestures

Conversation

@shawn-makes-stuff

Copy link
Copy Markdown
Contributor

Reopened from #326, which closed when I deleted my fork by accident. Same feature, rebased onto current main and fitted to the CC1/CC2 split. Earlier discussion is in #326.

Knock gestures: knock a rhythm on the frame to run a macro

Knock on the printer frame and COSMOS runs a macro. Uses the accelerometer already on the toolhead, no extra hardware. Only active while the printer is idle. Centauri Carbon 1 only (it uses the LIS2DW12 tap engine; the CC2 has a LIS3DH).

How it works

The LIS2DW12 on the toolhead has a hardware tap detector. While the printer is idle the module runs it at 1600 Hz; every knock latches a flag, and a small poll reads the flag 20 times a second, counts knocks and times the gaps between them. A second of silence closes the pattern. Patterns are matched by the proportions of their gaps, so the same rhythm knocked faster or slower still matches (tolerance configurable, 35% default). Two-knock patterns compare short vs long gap instead.

Recording is knock-and-confirm: knock the rhythm, the console asks for it again, a match saves it. Recorded patterns live in /user-resource/knock/knock.json and take precedence over the built-in [knock_pattern] sections.

Commands

One console command, KNOCK:

KNOCK list patterns and state
KNOCK ENABLED=0 / ENABLED=1 feature off / on, persists
KNOCK CALIBRATE=1 learn the user's tempo (5 quick knocks twice, then 3 with pauses)
KNOCK RECORD=1 NAME=x MACRO=<gcode> record a pattern bound to a macro (MACRO="M117 hi" with quotes for arguments)
KNOCK RECORD=1 NAME=x PARROT=1 ...bound to parrot mode: the toolhead replays the rhythm from the bed centre
KNOCK RECORD=1 NAME=x PRINT="text" ...bound to a console message; combines with MACRO or PARROT
KNOCK REMIND=x forgot a pattern? the toolhead plays it back
KNOCK DELETE=x remove a pattern

Built in: a double tap toggles the case and toolhead lights ([knock_pattern lights] in extras-readonly/knock.cfg).

Setup

  1. Idle printer, open the console. KNOCK shows "enabled". Knock the frame twice, lights toggle.
  2. KNOCK CALIBRATE=1, follow the prompts. Once.
  3. KNOCK RECORD=1 NAME=home MACRO=G28, knock a rhythm, knock it again. Done.

Performance and safety

  • Idle only. Gated on idle_timeout state and on the accelerometer not being in use by a measurement. During prints, moves and resonance tests the chip is powered down and the task sleeps on the idle_timeout:ready / idle_timeout:idle events. No polling, no SPI, no CPU while printing (one state check every 5 s). Measured on the CC1: klippy at its normal baseline while busy.
  • Idle cost: one SPI register read per 50 ms, about 2% of one core on the CC1. Memory is bounded (patterns dict, no sample buffers, no numpy).
  • Off switch: cosmos.conf [klipper] knock = False (default True on the CC1) removes the include, module not loaded. KNOCK ENABLED=0 at runtime.
  • Motion from a knock (parrot, remind, user macros) goes through normal gcode and only when idle.

Changes

  • recipes-apps/klipper/files/centauri-carbon-1/knock.py: the klippy extra, installed to klippy/extras/
  • recipes-apps/klipper/files/centauri-carbon-1/knock.cfg: [knock] options and the default lights pattern, installed to extras-readonly/
  • kalico_2026.02.00.bb: install both, CC1 only
  • config-manager: knock validator (falls back to False), knock = True in the CC1 default-machine.conf, and build-klipper-var-config.sh emits the include into zextras.cfg when True
  • docs/knock.md, README bullet

Notes for review

  • The module uses the lis2dw object's set_reg/read_reg and its bulk helper's is_started flag to detect measurements. Deliberate; commented in the source.
  • The poll runs as a reactor greenlet (like bulk_sensor) because SPI reads block.

🤖 Generated with Claude Code

New klippy extra (knock.py) that uses the toolhead LIS2DW12 tap engine to
detect knocks on the frame while the printer is idle. Knocks are grouped
into patterns matched by the proportions of the gaps between them, so a
pattern can be knocked faster or slower. One console command, KNOCK,
records patterns (knock, knock again to confirm), binds them to a macro,
a console message and/or "parrot" playback of the rhythm by the toolhead,
lists, deletes, reminds, calibrates tempo, and enables/disables.

Only runs while idle: during prints, moves and resonance tests the
accelerometer is powered down and the task sleeps on idle_timeout events.
Idle cost is one register read per 50 ms (about 2% of one core); memory
is bounded. Disabled via cosmos.conf [klipper] knock = False, in which
case the module is not loaded.

Ships a default double-tap that toggles the case and toolhead lights.
Recorded patterns persist in /user-resource/knock/knock.json.

This branch was successfully deployed

1 active deployment
approval-needed — 2a23ef95 Deployed Oct 11, 2026 by shawn-makes-stuff via approve #794
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant