Project 8: Split Keyboard (Wired Transport)

Build a wired split keyboard where two halves synchronize over UART or I2C and behave as a single logical device.

Quick Reference

Attribute Value
Difficulty Level 4: Advanced
Time Estimate 4-8 weeks
Main Programming Language C (QMK)
Alternative Programming Languages None
Coolness Level Level 4: Wizard
Business Potential Level 3: Ergo Builder
Prerequisites QMK firmware basics, matrix wiring
Key Topics Split transport, handedness, state sync

1. Learning Objectives

By completing this project, you will:

  1. Configure QMK split keyboard features.
  2. Implement a reliable UART or I2C transport between halves.
  3. Detect master/slave handedness reliably.
  4. Synchronize layers, LEDs, and key states.
  5. Handle transport failures gracefully.

2. All Theory Needed (Per-Concept Breakdown)

2.1 Split Transport, Handedness, and Electrical Integrity

Fundamentals

A split keyboard has two independent microcontrollers that must communicate to behave like one device. One half becomes the master and connects to the host via USB; the other half is a slave that scans its matrix and sends key state to the master. The transport is typically UART or I2C over a TRRS or JST cable. Handedness detection determines which half is master. Reliable communication requires correct wiring, proper pull-ups (for I2C), and signal integrity over the cable.

Deep Dive into the Concept

The split keyboard architecture is a small distributed system. Each half scans its local matrix and must report state to the master. The master merges both matrices and generates HID reports. This requires a communication protocol that is fast, reliable, and tolerant of occasional errors. QMK provides built-in split transport layers for serial (UART) or I2C. UART is simpler and often more robust over longer cables because it is point-to-point. I2C is open-drain and requires pull-up resistors; it can be more sensitive to noise and cable length.

Handedness detection is critical. If both halves think they are master, you get two USB devices or conflicting behavior. QMK supports several methods: MASTER_LEFT or MASTER_RIGHT (fixed), SPLIT_USB_DETECT (detect which half has USB), and EE_HANDS (store handedness in EEPROM). The best method depends on your hardware. USB detection is reliable if only one half has USB. EEPROM is flexible but requires flashing each half with different settings.

Electrical integrity matters because the transport is only as good as the wiring. For UART, you need RX and TX lines plus ground, and sometimes VCC if powering the slave from the master. You should include series resistors or protection if the cable is long. For I2C, the pull-up resistors are essential; without them, the bus will float and communication will fail. The cable should be short (often < 30 cm) to reduce capacitance. TRRS cables are common but can introduce cross-talk if the wiring is poor.

The transport protocol in QMK sends matrix state and some metadata (layer state, LED state). The frequency of updates is tied to the scan loop, so transport latency adds directly to overall keypress latency. This is why you must keep scan rates stable and avoid expensive features that slow down the master. If the slave half falls behind, the master may use stale matrix data, causing missed or delayed keypresses.

Robustness requires handling disconnections. If the cable is unplugged, the master should continue to work using its own matrix without crashing. QMK has options to handle timeouts and reset the transport. You should also provide a visual indicator (e.g., OLED or LED) that shows whether the slave is connected.

Additional transport details: ensure both halves share a common ground; without it, UART/I2C communication will be unstable or fail entirely. If you power the slave from the master, verify current draw and cable rating. For TRRS cables, avoid hot-plugging while powered if your design doesn’t include protection; shorting tip/ring during insertion can damage IO pins. Consider adding small series resistors (e.g., 100-220 ohm) on UART lines to reduce ringing. For handedness, EEPROM-based EE_HANDS is reliable but requires flashing each half separately; USB detection is easier but assumes only one USB connector.

Extra robustness: if you plan to customize transport, add a lightweight checksum or CRC to each packet so the master can detect corrupted data. QMK’s split transport is generally reliable, but noisy cables can cause intermittent errors. You can also enable a watchdog timer on both halves to recover from lockups. Always ensure the cable provides a solid ground connection; without a shared ground, the UART/I2C reference is undefined and communication will fail.

How this fits on projects

This concept is central to Project 8 and draws on matrix wiring from Project 3 and performance tuning from Project 6.

Definitions & key terms

  • Master: The half connected to USB that sends HID reports.
  • Slave: The half that sends matrix state to the master.
  • Transport: The communication link (UART or I2C).
  • Handedness: Which half is left or right and which is master.

Mental model diagram (ASCII)

Left half (master)  <---UART/I2C--->  Right half (slave)
     | USB
     v
   Host

How it works (step-by-step, with invariants and failure modes)

  1. Each half scans its local matrix.
  2. Slave sends matrix state to master over transport.
  3. Master merges matrices and sends HID report.
  4. Handedness determines which matrix is “left” or “right”.

Invariant: only one master. Failure modes include swapped TX/RX, missing pull-ups, or both halves acting as master.

Minimal concrete example

#define SPLIT_KEYBOARD
#define SERIAL_USART_FULL_DUPLEX
#define MASTER_LEFT

Common misconceptions

  • “I2C is always better”: UART is often more robust for split keyboards.
  • “Handedness doesn’t matter”: Without it, keymaps can be mirrored.
  • “Cable length doesn’t matter”: Longer cables increase errors.

Check-your-understanding questions

  1. Why is only one half the master?
  2. What hardware is required for I2C transport?
  3. What happens if the cable disconnects?

Check-your-understanding answers

  1. Only one USB device can represent the keyboard; the master aggregates data.
  2. Pull-up resistors and proper wiring of SDA/SCL and ground.
  3. The master should continue with its own matrix and mark the slave offline.

Real-world applications

  • Ergonomic split keyboards (Lily58, Kyria, Corne).
  • Distributed input devices.

Where you’ll apply it

  • In this project: §3.2 Functional Requirements and §5.10 Phase 1.
  • Also used in: Project 11.

References

  • QMK docs: split keyboard guide.
  • “Making Embedded Systems” Ch. 6-7.

Key insights

A split keyboard is a small distributed system; transport reliability is the foundation.

Summary

Split keyboards depend on reliable communication, correct handedness, and stable scan timing. These are hardware and firmware problems combined.

Homework/Exercises to practice the concept

  1. Sketch wiring for UART and I2C split transports.
  2. List the pros/cons of MASTER_LEFT vs SPLIT_USB_DETECT.

Solutions to the homework/exercises

  1. UART uses TX/RX/GND; I2C uses SDA/SCL/GND with pull-ups.
  2. MASTER_LEFT is fixed and simple; USB detect is automatic but requires USB on one half.

2.2 Matrix Merging, State Sync, and Latency Budget

Fundamentals

The master half must merge two matrices into a single logical map. This involves combining row/col data and synchronizing layer state, LED indicators, and other features. Latency is the sum of scan time on the slave, transport time, and master processing. If any part is slow, the keyboard feels laggy. State synchronization is also critical: both halves should show the same layer and LED status. If not, the user experience becomes confusing.

Deep Dive into the Concept

Matrix merging in a split keyboard is conceptually simple but timing-sensitive. The master has its own matrix data and receives the slave’s matrix data over transport. These two matrices are concatenated or mapped into a single logical matrix defined by the keymap. QMK uses a matrix split configuration that defines how the left and right matrices are combined. The keymap is written as if the keyboard were a single matrix, but internally QMK must map the slave’s row/col indices into the global matrix positions.

Latency is a budget. Suppose the scan loop runs at 1 kHz (1 ms per scan). The slave scans its matrix, then sends the state to the master. The transport might take 0.5 ms. The master then merges and produces a report. The worst-case latency for a key pressed on the slave could be close to 2 ms or more, depending on alignment with the scan cycle. If you add heavy RGB or OLED rendering, the scan loop could slow to 5 ms, and latency becomes noticeable. This is why performance tuning in Project 6 matters for split keyboards.

State synchronization includes layers, caps lock, and any custom indicators. In QMK, the master can broadcast layer state to the slave so that the slave’s OLED or LEDs show the correct status. If you do not enable this sync, the slave may show outdated information or remain on a default layer display. QMK provides SPLIT_LAYER_STATE_ENABLE and related options to keep state consistent.

Failure modes include partial updates: if the slave sends a matrix update but the layer state is stale, a key could be interpreted in the wrong layer. This is rare but can happen with poorly synchronized updates. A good design ensures that layer state changes are transmitted immediately and that the master merges using the latest state.

Another subtle issue is “stuck” keys when a cable disconnects mid-press. The master might think a key is still pressed because it never received a release event from the slave. QMK provides timeouts to clear stale state. You should test this explicitly by unplugging the cable while holding a key and confirming that the master clears it after a timeout.

Additional latency insight: when merging matrices, remember that the slave scan may be one or more cycles behind the master. If you want tighter latency, you can bias the master to wait for the slave update before sending the HID report, but this can slow the master keys. A practical compromise is to process master keys immediately and allow slave keys to appear one scan later. Document this behavior so users understand the slight asymmetry. Also, if you add per-half OLED indicators, ensure they subscribe to the synchronized layer state rather than local state to avoid mismatches.

Extra latency measurement: a simple way to measure split latency is to toggle a GPIO on the slave when a key is pressed and toggle another GPIO on the master when the key event is processed. Measure the time difference with a logic analyzer. This gives you a concrete latency budget and lets you quantify the impact of features like RGB or OLED. If you see large spikes, reduce scan load or transport frequency.

How this fits on projects

This concept is central to Project 8 and will inform Project 11 where you must define QA tests for split keyboards.

Definitions & key terms

  • Matrix merge: Combining left and right matrices into one logical matrix.
  • Latency budget: Total allowed time from press to report.
  • State sync: Keeping layers and indicators consistent across halves.

Mental model diagram (ASCII)

Slave scan (1ms) -> transport (0.5ms) -> master merge (0.2ms) -> HID

How it works (step-by-step, with invariants and failure modes)

  1. Slave scans its matrix and sends state.
  2. Master receives and merges with its own matrix.
  3. Master updates layer and LED state and optionally sends it back.
  4. HID report is sent to host.

Invariant: merged matrix reflects latest states from both halves. Failure modes include stale data on disconnect and desynced layers.

Minimal concrete example

#define SPLIT_LAYER_STATE_ENABLE
#define SPLIT_LED_STATE_ENABLE

Common misconceptions

  • “Split just duplicates the matrix”: It requires merging and synchronization.
  • “Latency is negligible”: Transport adds real delay.
  • “Layer sync is optional”: Without it, UI feedback is confusing.

Check-your-understanding questions

  1. Why does a key on the slave have higher latency?
  2. What happens if layer state isn’t synchronized?
  3. How do you handle a cable disconnect mid-press?

Check-your-understanding answers

  1. The state must travel over transport before the master can process it.
  2. The slave may show the wrong layer or send wrong keycodes.
  3. Use timeouts to clear stale key states.

Real-world applications

  • Ergonomic keyboards with split halves and LED/OLED indicators.

Where you’ll apply it

  • In this project: §3.3 Non-Functional Requirements and §6 Testing Strategy.
  • Also used in: Project 11.

References

  • QMK docs: split keyboard synchronization.

Key insights

Split keyboards succeed when you treat latency and synchronization as first-class design constraints.

Summary

The master must merge matrices and synchronize state with tight timing constraints. Test latency and failure modes explicitly.

Homework/Exercises to practice the concept

  1. Calculate worst-case latency for a slave key with 1 ms scan and 0.5 ms transport.
  2. Unplug the cable while holding a key and predict expected behavior.

Solutions to the homework/exercises

  1. Worst-case is just under 2 ms plus processing overhead.
  2. The master should clear the pressed state after a timeout and stop reporting the key.

3. Project Specification

3.1 What You Will Build

A wired split keyboard configuration that:

  • Uses UART or I2C transport.
  • Correctly detects master half.
  • Synchronizes layer and LED state.
  • Handles cable disconnects gracefully.

3.2 Functional Requirements

  1. Transport: UART or I2C configured and working.
  2. Handedness: Correct left/right detection.
  3. State sync: Layers and LED state mirrored.
  4. Reliability: Survive disconnect without stuck keys.

3.3 Non-Functional Requirements

  • Latency: End-to-end latency < 5 ms.
  • Reliability: No dropped key events during normal typing.
  • Maintainability: Clear config flags and documentation.

3.4 Example Usage / Output

QMK Console:
[split] master detected on left
[split] slave connected
[split] transport: serial

3.5 Data Formats / Schemas / Protocols

  • QMK split transport packets (internal).

3.6 Edge Cases

  • Cable disconnect mid-press (stuck key risk).
  • Slave not detected (wrong wiring).
  • Both halves attempt to be master.

3.7 Real World Outcome

A split keyboard that behaves like a single device.

3.7.1 How to Run (Copy/Paste)

qmk compile -kb splitkb/kyria -km splitmaster
qmk flash -kb splitkb/kyria -km splitmaster

3.7.2 Golden Path Demo (Deterministic)

  • Master detected on left.
  • Slave connects and sends matrix data.

3.7.3 If CLI: exact terminal transcript

$ qmk compile -kb splitkb/kyria -km splitmaster
Creating hex file: .build/splitkb_kyria_splitmaster.hex
exit_code=0

$ qmk console
[split] master detected on left
[split] slave connected
exit_code=0

$ qmk console
[split] slave timeout
exit_code=1

4. Solution Architecture

4.1 High-Level Design

Slave Matrix -> Transport -> Master Merge -> HID

4.2 Key Components

Component Responsibility Key Decisions
Transport Send matrix data UART vs I2C
Handedness Determine master USB detect vs fixed
State Sync Share layers/LEDs Enable sync flags
Timeout Logic Clear stale states on disconnect Timeout duration

4.3 Data Structures (No Full Code)

typedef struct {
    uint8_t matrix_rows[ROWS];
    uint8_t layer_state;
} split_packet_t;

4.4 Algorithm Overview

Key Algorithm: Merge Matrices

  1. Read local matrix.
  2. Receive remote matrix.
  3. Combine into global matrix state.
  4. Process keycodes and send HID report.

Complexity Analysis:

  • Time: O(R * C) per scan
  • Space: O(R * C)

5. Implementation Guide

5.1 Development Environment Setup

qmk setup
qmk doctor

5.2 Project Structure

keyboards/<split>/
├── config.h
├── rules.mk
└── keymaps/splitmaster/
    └── keymap.c

5.3 The Core Question You’re Answering

“How do two separate matrices behave like one keyboard?”

5.4 Concepts You Must Understand First

  1. Split transport wiring and handedness detection.
  2. Matrix merging and latency.
  3. State synchronization.

5.5 Questions to Guide Your Design

  1. Which transport is more reliable for your cable length?
  2. How will you indicate slave connection status?
  3. What timeout is acceptable for clearing stale keys?

5.6 Thinking Exercise

Calculate worst-case latency for a slave key if scan rate is 1 ms and transport adds 0.5 ms.

5.7 The Interview Questions They’ll Ask

  1. How does QMK decide the master half?
  2. What are the pros/cons of UART vs I2C for split keyboards?
  3. How do you prevent stuck keys on disconnect?

5.8 Hints in Layers

Hint 1: Use fixed handedness first Start with MASTER_LEFT before trying auto-detect.

Hint 2: Keep transport simple Use UART for initial bring-up.

Hint 3: Enable sync flags Turn on layer and LED sync.

Hint 4: Test disconnects Unplug cable mid-press to verify timeout handling.

5.9 Books That Will Help

Topic Book Chapter
Embedded communication “Making Embedded Systems” Ch. 6-7
Digital logic “Digital Design and Computer Architecture” Ch. 1-2

5.10 Implementation Phases

Phase 1: Transport Bring-up (2-3 weeks)

Goals: reliable communication between halves

Tasks:

  1. Wire UART/I2C and verify signals.
  2. Enable SPLIT_KEYBOARD and transport flags.

Checkpoint: Slave detected in QMK console.

Phase 2: State Sync (2-3 weeks)

Goals: layer and LED state synced

Tasks:

  1. Enable layer sync flags.
  2. Test layer changes on both halves.

Checkpoint: Both halves show same layer.

Phase 3: Robustness (1-2 weeks)

Goals: handle disconnects and latency

Tasks:

  1. Add timeout handling.
  2. Measure latency from slave.

Checkpoint: No stuck keys on disconnect.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Transport UART vs I2C UART more robust over cable
Handedness method MASTER_LEFT vs USB USB detect automatic detection
Timeout duration 100 ms vs 500 ms 200 ms clears stuck keys fast

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Transport Tests Verify link stability QMK console logs
Sync Tests Verify layer and LED sync layer toggles
Failure Tests Disconnect handling cable unplug mid-press

6.2 Critical Test Cases

  1. Slave connects and sends matrix data.
  2. Layer changes on master reflected on slave.
  3. Cable disconnect clears stuck keys within timeout.

6.3 Test Data

Transport: UART
Timeout: 200 ms

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
RX/TX swapped Slave never detected Swap wiring
Missing I2C pull-ups I2C transport fails Add 4.7k pull-ups
Both halves master Two USB devices or conflict Fix handedness configuration

7.2 Debugging Strategies

  • Use a scope to verify UART/I2C signal integrity.
  • Enable QMK console for split logs.

7.3 Performance Traps

RGB and OLED on both halves can slow scanning. Disable heavy effects for stability.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a LED indicator for slave connection.
  • Add a simple split status message on OLED.

8.2 Intermediate Extensions

  • Implement per-half RGB effects.
  • Add auto-rotation of OLED based on handedness.

8.3 Advanced Extensions

  • Implement custom transport protocol with CRC.
  • Add hot-plug detection with graceful recovery.

9. Real-World Connections

9.1 Industry Applications

  • Ergonomic split keyboards.
  • Distributed control panels.
  • SplitKB boards (Kyria, Lily58).
  • QMK split transport implementations.

9.3 Interview Relevance

  • Demonstrates embedded communication and synchronization skills.

10. Resources

10.1 Essential Reading

  • QMK docs: split keyboard guide.
  • “Making Embedded Systems” Ch. 6-7.

10.2 Video Resources

  • Split keyboard build tutorials.

10.3 Tools & Documentation

  • Logic analyzer, QMK console.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain UART/I2C split transport differences.
  • I can describe how matrices are merged.

11.2 Implementation

  • Slave connects reliably.
  • Layer state syncs across halves.

11.3 Growth

  • I can handle disconnects without stuck keys.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Split keyboard communicates over UART or I2C.
  • Master and slave detected correctly.

Full Completion:

  • Layer and LED state sync across halves.
  • Disconnect handling works.

Excellence (Going Above & Beyond):

  • Custom transport protocol with CRC.
  • Performance metrics and latency report.