Medius - Native APIQuickstart

Quickstart

Plug in and send your first command

A Medius box sits inline between a USB device and a PC. The real device passes through, and your program sends input of its own (cursor and buttons for a mouse, keys and media for a keyboard) over a USB-serial link. Below: wire it, open the link, send one movement command. Every step is byte-exact firmware behavior.

The box talks in frames, small fixed-shape packets that each carry one command. Most are fire-and-forget: you send and move on with no reply. The exception is QUERY, which gets one answer back.

Wire it up

The safe 3-port layout
  • USB1 (clone) → game PC
  • USB2 (control) → control PC
  • USB3 (mouse) → real mouse

The clone copies the real mouse's USB identity, so the game PC sees the same device it would if the mouse were plugged in directly.

Never connect USB1 and USB3 to the same machine. The USB3 5V rail can't be pulled low in firmware, so wiring both to one machine back-feeds power and can force a shutdown and drain the battery. One game PC, one control PC, never the same machine.

Full port map and hazard detail on Hardware.

Open the link

Open at the fixed baud, speak binary

Open /dev/ttyACM0 (Linux) or COMx (Windows) at 4,000,000 baud, 8N1, and speak binary immediately. There's no 115200 handshake and no baud-switch frame.

RESP is the box's reply to a question. To learn the protocol version:

  1. On first contact the box sends one RESP(VERSION) on its own, with SEQ 0, your ready signal. Or send QUERY(VERSION) yourself.
  2. Read proto_ver from the VERSION payload, the one-byte protocol version the firmware speaks.
  3. Check proto_ver == 3 before trusting the commands here. This documents version 3.

Serial framing detail on Transport; handshake and presence detail on Connection.

Send a MOVE

Relative cursor movement

MOVE nudges the cursor on the PC. Its frame has the standard shape, laid out byte-by-byte below.

  • SEQ is a number you pick and increment per frame. For a QUERY the box echoes it in the matching RESP so you can pair reply to request; here 0.
  • CRC16 lets the box reject a corrupted frame. It is CRC16-CCITT (polynomial 0x1021, initial value 0xFFFF) over TYPE | SEQ | LEN | PAYLOAD, stored little-endian.
import struct

def crc16_ccitt(data):
    crc = 0xFFFF
    for b in data:
        crc ^= b << 8
        for _ in range(8):
            crc = (crc << 1) ^ 0x1021 if crc & 0x8000 else crc << 1
            crc &= 0xFFFF
    return crc

def encode(type, seq, payload):
    head = bytes([type, seq]) + struct.pack('<H', len(payload)) + payload
    return bytes([0xA5]) + head + struct.pack('<H', crc16_ccitt(head))

frame = encode(0x01, 0, struct.pack('<Bhh', 0, 100, 0))
port.write(frame)

MOVE has opcode 0x01. Its payload is a motion byte (0 = cursor) then two signed 16-bit deltas, dx then dy. +x is right, +y is down. The example moves 100 right, 0 down.

That builds the bytes A5 01 00 05 00 00 64 00 00 00 <crc>: start byte, opcode 0x01, sequence, length, the motion/dx/dy payload, then the checksum. The byte-by-byte breakdown is on MOVE, the frame format on Frame Format.

Confirm

Check the chain before relying on injection

Injection is the input your program sends on top of the real mouse's passthrough. Confirm the chain (real mouse → box → PC) is live before you rely on it: send QUERY(HEALTH), read the flags byte from the RESP, and check these bits are set.

FlagMaskMeans
LINK_UP0x01The link to the host chip is up.
MOUSE_ATTACHED0x02A mouse is on USB3.
CLONE_CONFIGURED0x04The game PC has enumerated the clone.

A flag is set when (flags & mask) is non-zero. With all three set, your MOVE reaches the game PC. The full byte is on HEALTH.

Skip the framing

For a ready-made client, cargo add medius. See the library.