ESP-IDF with TinyUSB/USB Host support. Build separate left and right images. Target 1 ms HID servicing; pin tasks and USB callbacks to avoid command-parser jitter.
Pin map
Table
Side
Function
Pin
Left U8
USB D-/D+
GPIO19/GPIO20
Left U8
inter-MCU TX/RX
GPIO17/GPIO18
Left U8
inter-MCU RTS/CTS
GPIO16/GPIO15
Right U9
USB host D-/D+
GPIO19/GPIO20
Right U9
inter-MCU RX/TX
GPIO18/GPIO17
Right U9
inter-MCU CTS/RTS
GPIO15/GPIO16
Right U9
CH343 RX/TX
RXD0/TXD0
Right U9
CH343 RTS/CTS
GPIO4/GPIO5
Right U9
OLED I2C SDA/SCL
GPIO6/GPIO7
Control OLED (OLED1)
Hardware: Flux library OLED 128 x 64 0.96inch SSD1306-compatible 4-pin I2C module assembly, 128x64 monochrome, approximately 25.7 x 34.5 mm footprint envelope. The library assembly has no integrated I2C pull-ups, so R19/R20 provide 4.7 kΩ pull-ups to U9's isolated 3.3 V rail (Net 6); C40 is the local 100 nF ceramic bypass.
U9 mapping: OLED_SDA = GPIO6, OLED_SCL = GPIO7. These are unused non-strapping GPIOs and do not conflict with USB GPIO19/20, inter-MCU GPIO15-18, CH343 GPIO4/5 plus UART0, boot GPIO0, GPIO3/45/46 straps, GPIO26 PSRAM, or JTAG GPIO39-42.
I2C address: 7-bit 0x3C (probe at boot and fail non-fatally if absent). Configure ESP-IDF I2C master at 400 kHz, 3.3 V open-drain operation, with internal pull-ups disabled because R19/R20 are fitted.
Initialize I2C after GPIO/power validation and before starting the UI task. Initialize SSD1306 for 128x64, charge-pump enabled, normal/non-inverted display, rotation selected so text is readable from the USB connector edge. Clear the framebuffer and perform one full refresh. The UI task must be lower priority than USB host/device and inter-MCU servicing.
Maintain a RAM framebuffer and update changed regions at 5-10 Hz; cap full-screen refreshes to 10 Hz. Serialize I2C access with a mutex and use bounded timeouts. A missing or failed OLED must log an error and disable only the UI task, never block mouse forwarding or command parsing.
Required screen fields: firmware version/build; active command interface baud rate/speed; PC1 and PC2 attached/enumerated/suspended status; physical mouse connected/disconnected, polling interval/rate, and last-report activity; current operating mode (MAKCU, KMBox B/B+, or KMBox NET) plus parser/session state. Use concise labels and page/alternate fields if all data cannot remain legible simultaneously.
Trigger immediate dirty-region updates on USB connect/disconnect, mouse enumeration/poll-rate changes, command baud changes, or operating-mode/session changes; otherwise refresh status counters periodically. Do not update the display from USB callbacks—post state changes to the UI queue.
OLED power budget
Budget OLED1 at 30 mA typical on Net 6. U6 (AP63203WU-7) is rated for 2 A continuous output, so the display consumes 1.5% of regulator rating and leaves 1.97 A of nameplate output capacity before existing loads.
Referred to 5 V at an 85% conservative buck efficiency, the OLED adds approximately (3.3 V x 0.030 A)/(5 V x 0.85) = 23 mA to the protected PC2 VBUS path.
F3 (Littelfuse 1206L200PR) is 2.0 A hold at 20 C (1.5 A at 60 C, 1.1 A at 85 C). Against the existing documented 1.20 A PC2 operating budget, the revised conservative input budget is about 1.223 A, leaving about 0.777 A at 20 C and 0.277 A at 60 C. This remains compatible with the existing requirement for a USB-C source advertising at least 1.5 A. Do not claim the full 1.20 A system budget at 85 C; thermal derating of F3 limits that case and must be handled by a lower load budget or higher-temperature-rated protection part.
Startup
Validate 3V3 and reset causes.
Initialize logging at 115200.
Initialize inter-MCU UART at 4,000,000 baud with hardware flow control and framed messages with length, type, sequence and CRC.
Right: start CH343 command UART and USB host; parse descriptors and normalize HID reports.
Left: start TinyUSB device using forwarded descriptors with safe fallback descriptors.
Right: start optional Wi-Fi provisioning and KMBox NET-compatible UDP listener without delaying USB host startup.
Exchange readiness/version/capability messages, then enable report forwarding.
Clean-room protocol parser
Accept ASCII commands terminated by CRLF. Support public command families documented in MAKCU Compatibility Protocol. Keep parser and reply formatter table-driven. Preserve tracked command IDs when present. Unknown commands return a deterministic firmware-compatible error/prompt selected by compatibility tests; do not infer proprietary internals.
Implement separate table-driven parsers for MAKCU ASCII and KMBox B/B+ ASCII, plus a binary KMBox NET UDP decoder. Convert all accepted commands into a shared MouseInjectionEvent queue containing sequence, timestamp, movement, wheel, button set/clear masks, lock/mask state and requested duration/trajectory. Never let a parser write HID reports directly.
Physical mouse forwarding pipeline
Enumerate J3 as USB host on U9 and fetch the complete device/configuration/HID/report/string descriptors.
Build a descriptor manifest and send it to U8 over the CRC-protected inter-MCU link.
U8 presents the closest standards-valid copy of the physical mouse identity and report descriptor to PC1.
U9 forwards raw input reports with report ID and microsecond timestamp. Do not modify physical reports before forwarding.
U8 decodes only the fields needed for command merging, keeps raw physical state separately, applies injected movement/buttons and lock rules, then emits a report matching the presented descriptor.
On queue pressure, coalesce only relative movement deltas; never coalesce away button edges, wheel events, feature reports or connect/disconnect events.
On either MCU reset or link timeout, emit an all-buttons-released report before re-enumeration.
Acceptance target: byte-identical physical reports when no injection/lock is active and the source descriptor can be mirrored directly; otherwise field-identical semantic reports with conserved X/Y/wheel deltas and exact button transitions.
Compatibility test gates
Run public MAKCU client libraries unchanged at 115200 and 4 Mbaud.
Run public KMBox B/B+ scripts unchanged for movement, click/hold/release, wheel, query, mask/lock and baud/device commands.
Run a public KMBox NET client against the Wi-Fi UDP endpoint and verify connect/session sequence plus all public mouse command constants.
Capture at least 10,000 physical reports from several mice and compare source versus PC1 output with injection disabled; require zero lost/reordered button edges and zero net X/Y/wheel error.
Stress simultaneous 1 kHz physical reports and maximum-rate command injection for 30 minutes; require no USB disconnect, stuck button or parser starvation.
Verify hot-plug, MCU reset, PC suspend/resume and malformed-command recovery.
Timing
Use a 1 kHz report scheduler, bounded queues, and coalescing only where public smooth/silent semantics allow. Button transitions must not be dropped. Descriptor/control traffic has priority during enumeration; command batches must not starve USB host servicing.
Wi-Fi/UDP work runs at lower priority than USB host/device and inter-MCU tasks. Network loss or malformed NET packets must not affect physical mouse passthrough.