OPTIONOne generic, persistent option

OPTION carries an id byte then an id-specific value. Opcode 0x11.

OPTION 0x11 · payload 1 + value bytes

Fire-and-forget

PAYLOAD
OffsetFieldTypeNotes
0idu8option
1..valuevariesid-specific, delimited by the frame LEN

No reply. Read any value back with QUERY(OPTIONS, id).

IMPERFECTClone anyway, and admit the advanced control layer

id 0 · [allow u8]
ALLOW
ValueEffect
0Faithful-only: refuse a device the box can't clone faithfully (default)
1Clone the device anyway (every interface the box can serve byte-faithful), and admit the gated commands below
GATES
ItemOpt-in off
a device the box can't clone faithfullyRefused: no clone appears.
a forced rate, EMIT's force_hzNot applied.
REWRITEDiscarded, except the whole-table clear. Turning the opt-in off clears the table.
PATCHStored, not served. APPLY is ignored. Turning the opt-in off presents a patched clone again without its set.
TRANSFERReplied with status 0xFC.
RAW, and a clip's raw and transfer itemsDiscarded. Turning the opt-in off drops queued clip transfers.
a clip packet trigger that consumesRefused. Turning the opt-in off removes the ones the box holds.

A device the box can't clone faithfully has any of these: >4 IN endpoints live at once; >6 HID interfaces; high speed; configurations the box did not capture; a vendor bulk or isochronous endpoint; a setting carrying more endpoint descriptors than it declares (what fits is cloned, the rest left undriven); a report descriptor truncated or never captured.

The ESP32-S3 has four transmit FIFOs for IN endpoints other than endpoint 0. Those endpoints can be numbered up to 6, and a control endpoint above 0 counts as one.

With more live at once, the endpoints the box binds as mouse, keyboard and media keep a FIFO each. The rest share the remaining FIFOs, so a report on a shared one can go ≥1 poll late while every shared FIFO is in use.

If a device the box can't clone faithfully is attached, or a forced rate is pending, changing this option reboots the device chip to re-clone. Otherwise, the clone is presented again when the change alters the patch set it serves.

Read QUERY(OPTIONS, 0) (opt-in plus the over-capacity and imperfect-clone flags) · Library allow_imperfect_clones.

EXAMPLE

Opt in (allow = 1):

A5SOF
11TYPE
00SEQ
02 00LEN
00id
01allow
lo hiCRC16

MOVE_RIDEInject motion only on a native motion report

id 1 · [timeout u16 LE] ms
TIMEOUT
ValueEffect
0Off: injected motion is emitted on native reports and the box's reports alike (default)
N msInjected cursor and wheel motion rides only a native motion report seen within N ms. No synthetic motion frame. Motion left unridden is dropped (never dumped on the next move)

Injected motion keeps native report density.

While on, pure idle injection (moving the cursor while the real device is still) stops working: motion waits for a native motion report and is dropped if none comes.

Button, key, and media injection are unaffected. A move can opt out per command with the MOVE flags.

Changing the value drops any motion held for a ride and clears the standing bearing. Every with / against scale stops applying at that instant.

Read QUERY(OPTIONS, 1) · Library set_movement_riding.

EXAMPLE

Turn it on with a 20 ms window (timeout = 0x0014):

A5SOF
11TYPE
00SEQ
03 00LEN
01id
14 00timeout
lo hiCRC16

EMITInjection pace and wire rate

id 2 · [mode u8][rate_hz u16 LE][force_hz u16 LE]

Every OPTION(EMIT) writes all three fields. Resend the ones to keep.

MODE
ValueNamerate_hzEmit paced to
0Learnt (default)n/aThe measured native report rate
1Intervaln/aThe cloned mouse's declared poll rate (its bInterval)
2Fixedtarget Hzrate_hz, snapped to 1000/n

Fixed snaps to 1000/n Hz and caps at 1 kHz, so 1000, 500, 333 and 250 are exact (0 means 1000).

The pace is a ceiling. The box emits only while injection is pending.

FORCE_HZ
ValueEffect
0 (default)Serves the captured descriptor and polls the real device at the interval it declared
target HzWrites a power-of-two bInterval onto every HID interrupt-IN endpoint of the served descriptor: 1000/force_hz ms rounded to the nearest millisecond, then down to a power of two (600 Hz gives 500 Hz). Polls the real device at that same interval

A forced rate applies only with IMPERFECT on, because the descriptor stops matching the real device. Changing the resolved interval re-clones the box: the device chip reboots, and queries time out for a few seconds.

Vendor interfaces, interrupt-OUT and isochronous endpoints keep the captured value. A low-speed clone cannot express an interval <10 ms, and only powers of two are offered. Its floor is therefore bInterval 16, and any request >62 Hz resolves to 62.

An unknown mode discards the whole command, force_hz included, with no reply.

Read QUERY(OPTIONS, 2) (the stored fields, the rate in effect, and what the clone advertises) · Library set_emit_pace.

EXAMPLE

A fixed 1 kHz ceiling with the wire rate forced to 1 kHz (mode = 2, rate_hz = 0x03E8, force_hz = 0x03E8):

A5SOF
11TYPE
00SEQ
06 00LEN
02id
02mode
E8 03rate_hz
E8 03force_hz
lo hiCRC16

NAMEHuman-readable box name

id 3 · [name ascii 1..32] (0 bytes = clear)
VALUE
BytesEffect
1..32 printable ASCIISets the name to those bytes.
0 (the id alone)Clears the name, reverting to the MAC-derived Medius-XXXX default.

The name pairs with the box MAC and persists in NVS with no reboot. Read it as the ASCII tail of RESP(VERSION) after the MAC, not through QUERY(OPTIONS).

Read it back on RESP(VERSION) · Library set_name.

EXAMPLE

Name the box "Loki" (id = 3, ascii 4C 6F 6B 69):

A5SOF
11TYPE
00SEQ
05 00LEN
03id
4C 6F 6B 69name ascii
lo hiCRC16

BEARINGWhat with and against are measured against

id 4 · [window u16 LE] ms [mode u8]
WINDOW
ValueEffect
0No bearing is ever held, so with and against are inert whatever their scale
N msAn axis keeps the direction of its last injected delta for N ms (default 20)
MODE
ValueGeometry
0Per axis (default)
1Vector
2 or aboveUnknown: the whole command is dropped, window included, with no reply

A write that changes either field drops the standing bearing and the banked carry on every mouse interface. With a with / against scale live, the drop is a visible step in the emitted motion. Set the geometry before the scales, not between reports.

Read QUERY(OPTIONS, 4) · Library set_bearing.

EXAMPLE

A 20 ms window in vector mode (window = 0x0014, mode = 1):

A5SOF
11TYPE
00SEQ
04 00LEN
04id
14 00window
01mode
lo hiCRC16

RENDERMotion rendering texture

id 5 · [mode u8][full u8]

Every OPTION(RENDER) writes both fields. Resend the one to keep.

MODE
ValueNameEffect
0OffRenderer off. The box emits the paced fill.
1StockThe model's bit-exact triangular smoother. Its first report carries twice the steady delta.
2De-spiked (default)The same smoother, ramping up to the steady delta over 8 ms from the first report.
3UnsmoothedNo smoother. The model receives the raw injection.
one injected correction, drained over ~12 ms  (| = a report, . = an idle ms)

  mode = 0     | | | | | | | | | | | |      even fill at the paced rate
  mode > 0     | | . | . . | | . | . |      the native on/off texture
Aspectmode = 0mode > 0
Per-report deltaThe accumulator, split to fit the fieldShaped by the model, summing to the same total
ModelNoneABCurves (MIT), fit live per device
Before a profile armsEmits at onceEmits the paced fill, then switches over
FULL
ValueEffect
0 (default)Renders injected motion only. Native cursor delta is relayed byte for byte.
1Renders both: native cursor delta is taken out of the relayed report and joins injection as one stream through the model.

Rendering adds the smoother's group delay: 4 ms at mode = 1, 2.7 ms at mode = 2, none at mode = 3. With full at 1, native motion gets that delay plus 1 ms.

Per axis, a rendered report carries at most what the clone's cursor field holds (a bound never <127 counts). The model carries anything past that as debt into later reports.

The pace caps the rendered rate. The model's debt carries what the cap coalesces. Buttons, the wheel and every other field are relayed at native timing in both modes: the model renders cursor motion only.

A MOVE carrying any flag takes the plain paced path instead: NOW, FLUSH and DISCARD each fix exact timing, which the renderer otherwise sets. Clip cursor motion renders like a MOVE with no flag.

Under full, the rendered stream ignores MOVE_RIDE. Riding would hold native motion and drop what it never rides out. A clip's stream ignores it too.

The profile is built from native reports and never persisted. Every boot starts without one, and the profile arms from a window of native motion. full renders nothing while mode is 0, with no model in the path.

A mode above 3 or a full above 1 discards the whole command, with no reply.

Read QUERY(OPTIONS, 5) (both stored fields, and whether a profile has armed) · Library set_render.

EXAMPLE

De-spiked, rendering native motion too (mode = 2, full = 1):

A5SOF
11TYPE
00SEQ
03 00LEN
05id
02mode
01full
lo hiCRC16

SPREADInjected delta spread over time

id 6 · [percent u16 LE]

A host loop slower than the native report rate sends deltas worth several native reports. The percent is the share of the command interval over which the box releases each delta.

PERCENT
ValueEffect
0The whole delta is emitted on the next report.
1..99Released across that share of the interval, with the rounding remainder at the end of the share.
100 (default)Released evenly across one whole command interval.
101..65535Released over longer than the interval. Each command arrives on a remainder, so the box carries a standing backlog.
a 250 Hz host on a 1000 Hz native rate: one MOVE of 8 every 4 ms, 12 ms of wire

  ms             0  1  2  3  4  5  6  7  8  9 10 11
  percent = 0    8  .  .  .  8  .  .  .  8  .  .  .    one report per command, 4× the delta
  percent = 100  2  2  2  2  2  2  2  2  2  2  2  2    a report every ms at the native magnitude

The box learns the interval as the most common gap between MOVE arrivals. A burst doesn't move it, and a rate change is followed. Until the box has an interval, and for commands more than ~32 ms apart, the whole delta is emitted on the next report regardless of percent.

Aspectpercent = 0percent = 100
Delivered totalExactExact
Added latencyNoneHalf the interval on average (~4 ms at 125 Hz)
Reports per commandOneUp to one per report the box emits in the interval, and never more than the delta's count
Wheel motionNot spreadNot spread: a detent is one unit

A loop at the native report rate keeps each command whole. The command waits in a short queue and is emitted alone on one report, no later than the first report after its interval ends. A delta smaller than its interval's report count can't be split one count per report. It is emitted once, part way through.

A MOVE carrying any flag is not spread: NOW, FLUSH and DISCARD each fix exact timing. FLUSH and DISCARD also act on any still-waiting part of an earlier delta, so they mean the same at any percent.

SPREAD and RENDER compose independently. Under full rendering, only injected motion is spread. Native motion already arrives at the native report rate.

Read QUERY(OPTIONS, 6) (the percent, and the interval in effect) · Library set_spread.

EXAMPLE

One whole command interval (percent = 0x0064):

A5SOF
11TYPE
00SEQ
03 00LEN
06id
64 00percent
lo hiCRC16