Reference

MAKCU API

KM Host Protocol — v3.9 · Comprehensive command reference for the MAKCU ecosystem.

Protocol

Transport & Framing

All replies start with km. and end withCRLF followed by the prompt>>> .
TX samples below show the final prompt as\r\n>>> .
RX (Host → Device)
  • 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>
TX (Device → Host)
  • 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

Mouse Buttons

Individual Buttons (GET/SET)

Command
left([state]) | right([state]) | middle([state]) | side1([state]) | side2([state])
Params
state: 0=release (sends frame), 1=down, 2=silent_release (sets to 0 but doesn't send frame)
Response (GET)
km.left()
>>> 

Returns lock state: 0=none, 1=raw, 2=injected, 3=both.

Response (SET)
km.left(1)
>>> 

Echo ACK (subject to echo(0|1)).

Click Scheduling (SET)

Command
click(button[,count[,delay_ms]])
Params
button: button number; count: click count (optional); delay_ms: delay (optional, defaults to random 35-75ms, internal timing)
Response (SET)
km.click(1,3)
>>> 

Echo ACK.

turbo([button[,delay_ms]]) — GET/SET

Command
turbo([button[,delay_ms]])
Description

Enable rapid-fire mode for mouse buttons. When the button is held down, it automatically triggers rapid press/release cycles at the specified interval. Multiple buttons can have turbo enabled simultaneously, each with its own delay setting.

Params
  • button: 1-5 (mouse buttons: 1=left, 2=right, 3=middle, 4=side1, 5=side2), 0=disable all turbo
  • delay_ms: 1-5000ms (0=disable). The delay is the time between each press/release toggle
  • turbo() - Returns only active turbo settings as (m1=200, m2=400)
  • turbo(button) - Sets turbo with random 35-75ms delay
  • turbo(button, 0) - Disables turbo for that specific button
  • turbo(0) - Disables all turbo
Examples
km.turbo(1, 500)
>>> 
km.turbo(2, 250)
>>> 
km.turbo(1)
>>> 
km.turbo()
>>> 
Motion

Mouse Movement

move(dx,dy[,segments[,cx1,cy1[,cx2,cy2]]]) — SET

Command
move(dx,dy[,segments[,cx1,cy1[,cx2,cy2]]])
Params

dx,dy: relative move distance; segments: segment count (optional, default 1, max 512); cx1,cy1,cx2,cy2: cubic Bézier control points (optional)

Response (SET)
km.move(10,-3)
>>> 
km.move(100,50,8,40,25,80,10)
>>> 

Echo ACK.

moveto(x,y[,segments[,cx1,cy1[,cx2,cy2]]]) — SET

Command
moveto(x,y[,segments[,cx1,cy1[,cx2,cy2]]])
Description

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().

Params

x,y: absolute coordinates; segments: segment count (optional, default 1, max 512); cx1,cy1,cx2,cy2: cubic Bézier control points (optional)

Response (SET)
km.moveto(640,360)
>>> 
km.moveto(100,50,8,40,25,80,10)
>>> 

Echo ACK.

wheel(delta) — SET

Command
wheel(delta)
Description

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.

Params
delta: scroll step (clamped to ±1)
Response (SET)
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

Command
pan([steps])
Params
steps: horizontal scroll steps (SET); no params to query pending (GET)
Response
km.pan(3)
>>> 

tilt([steps]) — GET/SET

Command
tilt([steps])
Params
steps: tilt/z-axis scroll steps (SET); no params to query pending (GET)
Response
km.tilt(2)
>>> 

getpos() — GET

Command
getpos()
Response (GET)
km.getpos()
>>> km.getpos(123,456)
>>> 

Returns current pointer position as (x,y).

silent(x,y) — SET

Command
silent(x,y)
Params
Move then perform silent left click at (x,y)
Response (SET)
km.silent(400,300)
>>> 

Echo ACK.

Advanced

Mouse Advanced

mo(buttons,x,y,wheel,pan,tilt) — SET

Command
mo(buttons,x,y,wheel,pan,tilt)
Description

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.

Params
buttons: button mask; x,y: movement deltas; wheel,pan,tilt: scroll values
Response (SET)
km.mo(1,10,5,0,0,0)
>>> 

lock_<target>(state) — GET/SET

Command Format
lock_<target>([state])
Description

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.

Targets

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)
Response (GET)
km.lock_mx()
>>> km.lock_mx(1)
>>> 

Returns: 1=locked, 0=unlocked

Response (SET)
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

Command Format
catch_<target>([mode])
Description

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.

Targets
ml, mm, mr, ms1, ms2 (buttons only, not axes)
Params
mode: 0=auto, 1=manual; () returns state
Response (GET)
km.catch_ml()
>>> 

Use `()` to query catch state

Response (SET)
km.catch_ml(1)
>>> 

Enable manual catch for left mouse button

km.catch_ml(0)
>>> 

Enable auto catch for left mouse button

Remap

Mouse Remap (Physical Only)

The following commands only affect physical input, injection is NOT affected. This means you can remap physical mouse buttons and axes, while injected inputs via API will remain unchanged.

remap_button([src,dst]) — GET/SET

Command
remap_button([src,dst])
Description

Remap mouse buttons (physical only). Auto-clears conflicting mappings; both directions can be mapped (swap).

Params
  • () - Show only active mappings, e.g., (left:right,right:left)
  • (0) - Reset all button remaps
  • (src,dst) - Map button src→dst (1=left, 2=right, 3=middle, 4=side1, 5=side2)
  • (src,0) - Clear remap for button src
Examples
km.remap_button()
>>> 
km.remap_button(1,2)
>>> 
km.remap_button(0)
>>> 

remap_axis([inv_x,inv_y,swap]) — GET/SET

Command
remap_axis([inv_x,inv_y,swap])
Description

Remap mouse axes (physical only). Set all three flags at once.

Params
  • () - 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)
Examples
km.remap_axis()
>>> 
km.remap_axis(0,1,0)
>>> 
km.remap_axis(0)
>>> 

invert_x([state]) — GET/SET

Command
invert_x([state])
Description
Invert X axis (physical only)
Params
() show; (0) disable; (1) enable
Response
km.invert_x(1)
>>> 

invert_y([state]) — GET/SET

Command
invert_y([state])
Description
Invert Y axis (physical only)
Params
() show; (0) disable; (1) enable
Response
km.invert_y(1)
>>> 

swap_xy([state]) — GET/SET

Command
swap_xy([state])
Description
Swap X and Y axes (physical only)
Params
() show; (0) disable; (1) enable
Response
km.swap_xy(1)
>>> 
Keyboard

Keyboard Commands

Keyboard commands support two formats: numeric HID code (e.g., 4) or string key name (e.g., 'a', "enter"). Supported key names include letters, numbers, function keys, navigation keys, and modifiers. See specification for details.

down(key) — SET

Command
down(key)
Params
key: HID code or quoted string ('a', "shift")
Response (SET)
km.down('shift')
>>> 

up(key) — SET

Command
up(key)
Params
key: HID code or quoted string ('a', "ctrl")
Response (SET)
km.up("ctrl")
>>> 

press(key[,hold_ms[,rand_ms]]) — SET

Command
press(key[,hold_ms[,rand_ms]])
Params

key: HID code or quoted string; hold_ms: hold duration (defaults to random 35-75ms, logged); rand_ms: optional randomization window

Examples
km.press('a')
>>> 
km.press("enter")
>>> 
km.press('d', 50)
>>> 

string(text) — SET

Command
string(text)
Params
text: ASCII string to type
Response (SET)
km.string("hello")
>>> 

init() — SET

Command
init()
Description
Clear keyboard state and release pressed keys
Response (SET)
km.init()
>>> 

isdown(key) — GET

Command
isdown(key)
Params
key: HID code or quoted string ('a', "shift")
Response (GET)
km.isdown("ctrl")
>>> km.isdown(1)
>>> 

Returns 1 if key is down, 0 if released.

mask(key[,mode]) — GET/SET

Command
mask(key[,mode])
Params
key: HID code or quoted string; mode: 0=off, 1=on
Response
km.mask('a',1)
>>> 

remap(source,target) — GET/SET

Command
remap(source,target)
Params
source,target: both can be HID code or quoted string; target=0 clears remap (passthrough)
Examples
km.remap('a','b')
>>> 
km.remap('a',0)
>>> 
Streaming

Streaming

keys([mode[,period_ms]]) — GET/SET

Command
keys([mode[,period_ms]])
Description

Stream keyboard keys with human-readable names. Mode 1=raw (physical input), 2=constructed frame (after remapping/masking); period clamped 1-1000 ms.

Params
mode: 1=raw, 2=constructed frame; period_ms: 1-1000ms; () to query; (0) or (0,0) to reset
Output Format

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.

Response (GET)
km.keys(2,50)
>>> 
Response (SET)
km.keys(1,100)
>>> 

buttons([mode[,period_ms]]) — GET/SET

Command
buttons([mode[,period_ms]])
Params
1=raw, 2=constructed frame; time_ms: 1-1000ms; (0) or (0,0) to reset
Response (GET)
km.buttons(2,25)
>>> 
Response (SET)
km.buttons(1,10)
>>> 

When enabled, device emits 1-byte mask:

km.buttons<mask_u8>
>>> 

axis([mode[,period_ms]]) — GET/SET

Command
axis([mode[,period_ms]])
Params
1=raw, 2=constructed frame; period_ms: 1-1000ms; (0) or (0,0) to reset
Output Format

Output format: raw(x,y,w) or mut(x,y,w) where w is the wheel delta.

Response (GET)
km.axis(1,25)
>>> 
Response (SET)
km.axis(2,10)
>>> 

mouse([mode[,period_ms]]) — GET/SET

Command
mouse([mode[,period_ms]])
Modes
1=raw, 2=constructed frame; period_ms: 1-1000ms; () to query; (0) or (0,0) to reset
Response (GET)
km.mouse(2,25)
>>> 
Response (SET)
km.mouse(1,10)
>>> 
Streaming format

Device emits 8-byte binary frame (x,y as int16):

km.mouse<8 bytes>
>>> 
Screen

Position & Screen

screen([W,H]) — GET/SET

Command
screen([width,height])
Params
() to query; (width,height) to set virtual screen size
Response (GET)
km.screen(1920,1080)
>>> 
Response (SET)
km.screen(2560,1440)
>>> 
System

System Commands

help() — GET

Command
help()
Description
Show command list
Response (GET)
km.help()
>>> 

info() — GET

Command
info()
Description
Report system info: MAC address, MCU temperature (when available), RAM stats, firmware info, CPU, and uptime
Response (GET)
km.info()
>>> 

version() — GET

Command
version()
Description
Report firmware version
Response (GET)
km.version()
>>> 

device() — GET

Command
device()
Description
Report which device is used more: (keyboard), (mouse), or (none)
Response (GET)
km.device()
>>> 

fault() — GET

Command
fault()
Description

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.

Response (GET)
km.fault()
>>> 

reboot() — SET

Command
reboot()
Description
Reboot device (reboots after response)
Response (SET)
km.reboot()
>>> 

serial([text]) — GET/SET

Command
serial([text])
Description

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.

Params
() to query; (0) to reset; (text) to set sanitized serial value
Response (GET)
km.serial()
>>> km.serial("MAKCU001")
>>> 

Returns current serial number.

Response (SET)
km.serial("MAKCU001")
>>> 
km.serial(0)
>>> 

Echo ACK. The change is persistent across firmware updates.

Config

Configuration

log([level]) — GET/SET

Command
log([level])
Params
level: 0-5; empty to query current level
Response
km.log(3)
>>> 

echo([enable]) — GET/SET

Command
echo([enable])
Params
enable: 1=on, 0=off; empty to query
Response
km.echo(1)
>>> 

baud([rate]) — GET/SET

Command
baud([rate])
Params
rate: 115200 – 4000000; 0=reset to default 115200; empty to query
Response (GET)
km.baud(115200)
>>> 
Response (SET)
km.baud(921600)
>>> 

Applies immediately; host must re-open serial at new speed.

hs([enable]) — GET/SET

Command
hs([enable])
Description
USB high-speed compatibility for devices that may not report poll rate correctly *persistent
Params
() query; (1/0) enable/disable
Response
km.hs(1)
>>> 

release([timer_ms]) — GET/SET

Command
release([timer_ms])
Description

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.

Params
() get status; (timer_ms) set timer 500-300000ms, (0) disables
Response (GET)
km.release()
>>> 
Response (SET)
km.release(5000)
>>> 
km.release(0)
>>> 
No USB

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()
Binary

Baud Rate Change (Binary)

Purpose
Set UART baud rate using a binary frame (no ASCII command).
Frame
DE AD <lenLE:2> <cmd:0xA5> <baud_rate:LE32>
Example (115200)
DE AD 05 00 A5 00 C2 01 00

Breakdown: DE AD | 05 00(len=5) | A5 (cmd) |00 C2 01 00 (115200 LE)

Spec

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
API version 3.9makcu 2025