MAKCU API
KM Host Protocol — v3.9 · Comprehensive command reference for the MAKCU ecosystem.
Transport & Framing
km. and end withCRLF followed by the prompt>>> .\r\n>>> .- Command format: Start with . and end with ). The km. prefix is no longer required.
- Other values (such as km, \r\n, etc.) are ignored. Example: .move(1,1,)
- Optional binary frame: DE AD <lenLE:2> <ASCII or binary payload>
- Format: km.payload\r\n>>> (the km. prefix is retained for compatibility)
- Setters echo the input as ACK unless suppressed byecho(0).
- Some streaming replies carry binary after the km. prefix:km.mouse<8 bytes> andkm.buttons<1-byte mask>. Lines still terminate with CRLF and the prompt.
Mouse Movement
move(dx,dy[,segments[,cx1,cy1[,cx2,cy2]]]) — SET
dx,dy: relative move distance; segments: segment count (optional, default 1, max 512); cx1,cy1,cx2,cy2: cubic Bézier control points (optional)
km.move(10,-3) >>>
km.move(100,50,8,40,25,80,10) >>>
Echo ACK.
moveto(x,y[,segments[,cx1,cy1[,cx2,cy2]]]) — SET
Move pointer to absolute position. Internally calculates the needed x,y movement to reach the requested position on the screen. Parameters align with km.move().
x,y: absolute coordinates; segments: segment count (optional, default 1, max 512); cx1,cy1,cx2,cy2: cubic Bézier control points (optional)
km.moveto(640,360) >>>
km.moveto(100,50,8,40,25,80,10) >>>
Echo ACK.
wheel(delta) — SET
Windows does not allow multiple scroll steps in a single command. The delta value is clamped to ±1 step (positive for scroll up, negative for scroll down). Values greater than 1 are treated as 1, values less than -1 are treated as -1.
km.wheel(-1) >>>
km.wheel(1) >>>
Echo ACK. Note: km.wheel(-5) will only scroll down 1 step, km.wheel(5) will only scroll up 1 step.
pan([steps]) — GET/SET
km.pan(3) >>>
tilt([steps]) — GET/SET
km.tilt(2) >>>
getpos() — GET
km.getpos() >>> km.getpos(123,456) >>>
Returns current pointer position as (x,y).
silent(x,y) — SET
km.silent(400,300) >>>
Echo ACK.
Mouse Advanced
mo(buttons,x,y,wheel,pan,tilt) — SET
Send complete raw mouse frame (SET only, no GET). (0) clears all; x,y,wheel,pan,tilt are one-shots; button mask mirrors button states.
km.mo(1,10,5,0,0,0) >>>
lock_<target>(state) — GET/SET
Lock button or axis. The target is part of the command name, not a parameter. state: 1=lock, 0=unlock. Call with `()` to read lock state.
Buttons: ml/mm/mr/ms1/ms2
- ml - Left mouse button
- mm - Middle mouse button
- mr - Right mouse button
- ms1 - Side button 1
- ms2 - Side button 2
Axes: mx/my/mw/mx+/mx-/my+/my-/mw+/mw-
- mx/my/mw - Full lock (blocks all movement in that axis)
- mx+/my+/mw+ - Positive direction lock (blocks positive movement only)
- mx-/my-/mw- - Negative direction lock (blocks negative movement only)
km.lock_mx() >>> km.lock_mx(1) >>>
Returns: 1=locked, 0=unlocked
km.lock_mx(1) >>>
Lock all X-axis movement
km.lock_mx+(1) >>>
Lock only positive X movement (right)
km.lock_mx-(1) >>>
Lock only negative X movement (left)
km.lock_my(1) >>>
Lock all Y-axis movement
km.lock_mw(1) >>>
Lock all wheel movement
km.lock_mw+(1) >>>
Lock only positive wheel movement (scroll up)
km.lock_mw-(1) >>>
Lock only negative wheel movement (scroll down)
km.lock_ml(1) >>>
Lock left mouse button
catch_<target>(mode) — GET/SET
Enable catch on a locked button. The target is part of the command name, not a parameter. Note: Catch only works for buttons, not axes. Requires corresponding km.lock_<target> to be set first.
km.catch_ml() >>>
Use `()` to query catch state
km.catch_ml(1) >>>
Enable manual catch for left mouse button
km.catch_ml(0) >>>
Enable auto catch for left mouse button
Mouse Remap (Physical Only)
remap_axis([inv_x,inv_y,swap]) — GET/SET
Remap mouse axes (physical only). Set all three flags at once.
- () - Show current settings, e.g., (invert_x=0,invert_y=1,swap_xy=0)
- (0) - Reset all axis remaps
- (inv_x,inv_y,swap) - Set all three flags (0 or 1)
km.remap_axis() >>>
km.remap_axis(0,1,0) >>>
km.remap_axis(0) >>>
invert_x([state]) — GET/SET
km.invert_x(1) >>>
invert_y([state]) — GET/SET
km.invert_y(1) >>>
swap_xy([state]) — GET/SET
km.swap_xy(1) >>>
Keyboard Commands
down(key) — SET
km.down('shift')
>>> up(key) — SET
km.up("ctrl")
>>> press(key[,hold_ms[,rand_ms]]) — SET
key: HID code or quoted string; hold_ms: hold duration (defaults to random 35-75ms, logged); rand_ms: optional randomization window
km.press('a')
>>> km.press("enter")
>>> km.press('d', 50)
>>> string(text) — SET
km.string("hello")
>>> init() — SET
km.init() >>>
isdown(key) — GET
km.isdown("ctrl")
>>> km.isdown(1)
>>> Returns 1 if key is down, 0 if released.
mask(key[,mode]) — GET/SET
km.mask('a',1)
>>> remap(source,target) — GET/SET
km.remap('a','b')
>>> km.remap('a',0)
>>> Streaming
keys([mode[,period_ms]]) — GET/SET
Stream keyboard keys with human-readable names. Mode 1=raw (physical input), 2=constructed frame (after remapping/masking); period clamped 1-1000 ms.
Output format: keys(raw,shift,'h') or keys(constructed,ctrl,shift,'a') - modifiers and keys shown as names (e.g., 'shift', 'ctrl', 'h', 'a') instead of HID numbers.
km.keys(2,50) >>>
km.keys(1,100) >>>
axis([mode[,period_ms]]) — GET/SET
Output format: raw(x,y,w) or mut(x,y,w) where w is the wheel delta.
km.axis(1,25) >>>
km.axis(2,10) >>>
mouse([mode[,period_ms]]) — GET/SET
km.mouse(2,25) >>>
km.mouse(1,10) >>>
Device emits 8-byte binary frame (x,y as int16):
km.mouse<8 bytes> >>>
Position & Screen
screen([W,H]) — GET/SET
km.screen(1920,1080) >>>
km.screen(2560,1440) >>>
System Commands
help() — GET
km.help() >>>
info() — GET
km.info() >>>
version() — GET
km.version() >>>
device() — GET
km.device() >>>
fault() — GET
Returns stored parse fault information including ESP32 MAC address, failed endpoint address, interface number, failure reason, and raw HID descriptor bytes. Useful for debugging devices that fail to parse.
km.fault() >>>
reboot() — SET
km.reboot() >>>
serial([text]) — GET/SET
Used to change the serial number of an attached mouse or keyboard while connected to MAKCU. This change is persistent and remains even after future firmware changes. Note: MAKCU does not allow changing serial numbers for a device that does not contain one.
km.serial()
>>> km.serial("MAKCU001")
>>> Returns current serial number.
km.serial("MAKCU001")
>>> km.serial(0) >>>
Echo ACK. The change is persistent across firmware updates.
Configuration
log([level]) — GET/SET
km.log(3) >>>
echo([enable]) — GET/SET
km.echo(1) >>>
baud([rate]) — GET/SET
km.baud(115200) >>>
km.baud(921600) >>>
Applies immediately; host must re-open serial at new speed.
hs([enable]) — GET/SET
km.hs(1) >>>
release([timer_ms]) — GET/SET
Auto-release monitoring system. Continuously monitors independent lock, button, and key states. When the timer expires, it releases only the corresponding values that remain active (not all at once). This setting is persistently saved to storage and automatically enabled on startup/boot. `()` get status (0=disabled, else time ms); `(timer_ms)` set timer 500-300000ms (5 min), (0) disables.
km.release() >>>
km.release(5000) >>>
km.release(0) >>>
Functions That Work Without USB Device Attached
The following commands work without a USB device attached to USB 3:
- km.baud()
- km.echo()
- km.fault()
- km.help()
- km.info()
- km.log()
- km.reboot()
- km.screen()
- km.serial()
- km.version()
Baud Rate Change (Binary)
DE AD 05 00 A5 00 C2 01 00
Breakdown: DE AD | 05 00(len=5) | A5 (cmd) |00 C2 01 00 (115200 LE)
Limits & Parsing
- Bézier segments: max 512
- Movement: int16
- Wheel: int8
- Keyboard commands support numeric HID codes or quoted strings ('a', "enter")
- Supported key names: a-z, 0-9, enter, esc, tab, space, shift, ctrl, alt, gui, F1-F12, arrow keys, etc.
- echo(0) suppresses most setter echoes; GETs still reply
- Baud range: 115200–4000000; baud(0) resets to 115200