| name | keychron-hardware-design |
| description | Access and work with Keychron keyboard and mouse industrial design files (STEP, DXF, DWG, PDF) for 135+ models to create compatible accessories, remixes, and modifications. |
| triggers | ["open keychron hardware files","work with keychron cad models","modify keychron keyboard design","create keychron compatible accessory","remix keychron case or plate","access keychron step files","design keychron keyboard mod","export keychron design files"] |
Keychron Hardware Design Skill
Skill by ara.so — Design Skills collection.
This skill enables working with Keychron's production-grade hardware design files for 135+ keyboard and mouse models. The repository contains CAD assets in STEP, DXF, DWG, and PDF formats for cases, plates, keycaps, stabilizers, and full assemblies across all major Keychron series (Q, K, V, C, L, P, M, G).
What This Project Provides
- 135+ device models with industrial design files
- 734+ design files across multiple CAD formats
- Production-grade geometry for cases, plates, stabilizers, encoders, and keycaps
- Source-available license allowing personal use, education, and original compatible accessories
- Multiple keyboard series: Q Series, Q Pro, Q HE, Q Max, Q Ultra 8K, K Pro, K Max, K HE, K QMK, V Max, V Ultra 8K, V 8K, C Pro 8K, L Series, P HE
- Mouse series: M1-M7, G1-G2 with shell and full model files
Repository Structure
Keychron-Keyboards-Hardware-Design/
├── C-Pro-8K-Series/
│ ├── C1 Pro 8K/
│ ├── C2 Pro 8K/
│ └── C3 Pro 8K/
├── Q-Series/
│ ├── Q0 Plus/
│ ├── Q1/ through Q12/
│ ├── Q60/
│ └── Q65/
├── Q-Pro-Series/
│ └── Q1 Pro/ through Q14 Pro/
├── Q-HE-Series/
│ └── Q0 HE/ through Q12 HE/
├── Q-Max-Series/
│ └── Q0 Max/ through Q15 Max/
├── K-Pro-Series/
│ └── K1 Pro/ through K17 Pro/
├── K-Max-Series/
│ └── K0 Max/ through K17 Max/
├── K-HE-Series/
│ └── K2 HE/, K4 HE/, K6 HE/, K8 HE/, K10 HE/
├── K-QMK-Series/
│ └── K1 QMK/ through K10 QMK/
├── V-Max-Series/
│ └── V1 Max/ through V10 Max/
├── L-Series/
│ ├── L1/
│ └── L3/
├── P-HE-Series/
│ ├── P1 HE/
│ ├── P2 HE/
│ └── P3 HE/
├── Mice/
│ ├── M1/ through M7/
│ ├── G1/
│ └── G2/
├── Keycap Profiles/
│ ├── Cherry Profile/
│ ├── KSA/
│ ├── LSA/
│ ├── MDA/
│ ├── OEM/
│ └── OSA/
└── docs/
├── file-format-guide.md
├── getting-started.md
├── 3d-printing-guide.md
├── license-faq.md
└── CONTRIBUTING.md
Installation
Clone the repository to access design files:
git clone https://github.com/Keychron/Keychron-Keyboards-Hardware-Design.git
cd Keychron-Keyboards-Hardware-Design
For large repositories, use sparse checkout to download only specific models:
git clone --filter=blob:none --sparse https://github.com/Keychron/Keychron-Keyboards-Hardware-Design.git
cd Keychron-Keyboards-Hardware-Design
git sparse-checkout set "Q-Series/Q1"
File Formats and CAD Software Compatibility
STEP Files (.stp, .step)
Universal 3D CAD format for full geometry
Compatible software:
- FreeCAD (free, open source)
- Fusion 360 (free for personal use)
- SolidWorks (commercial)
- Onshape (cloud-based, free tier)
- Rhino (commercial)
- Blender (free, with CAD add-ons)
DXF/DWG Files
2D technical drawings for plates and cutouts
Compatible software:
- QCAD (free)
- LibreCAD (free, open source)
- AutoCAD (commercial)
- DraftSight (commercial)
PDF Files
Technical documentation and dimension references
Working with Design Files
Opening STEP Files in FreeCAD
import FreeCAD
import Part
doc = FreeCAD.open("/path/to/Keychron-Keyboards-Hardware-Design/Q-Series/Q1/Q1-Case.stp")
objects = doc.Objects
print(f"Loaded {len(objects)} objects")
for obj in objects:
if hasattr(obj, 'Shape'):
bbox = obj.Shape.BoundBox
print(f"Object: {obj.Label}")
print(f" Dimensions: {bbox.XLength:.2f} x {bbox.YLength:.2f} x {bbox.ZLength:.2f} mm")
Extracting Plate Dimensions from DXF
import ezdxf
doc = ezdxf.readfile("K-Pro-Series/K8-Pro/K8-Pro-Plate.dxf")
msp = doc.modelspace()
circles = msp.query('CIRCLE')
print(f"Found {len(circles)} circular features")
for circle in circles:
center = circle.dxf.center
radius = circle.dxf.radius
diameter = radius * 2
print(f"Circle at ({center.x:.2f}, {center.y:.2f}), diameter: {diameter:.2f}mm")
polylines = msp.query('LWPOLYLINE')
for polyline in polylines:
points = list(polyline.get_points())
print(f"Polyline with {len(points)} vertices")
Modifying Case Geometry in Python
from OCP import gp, BRepPrimitivAPI, BRepAlgoAPI, BRepBuilderAPI
import cadquery as cq
original_case = cq.importers.importStep("Q-Series/Q3/Q3-Case.stp")
post_height = 10.0
post_diameter = 5.0
post_positions = [(20, 20), (20, 100), (140, 20), (140, 100)]
for x, y in post_positions:
post = cq.Workplane("XY").circle(post_diameter / 2).extrude(post_height)
post = post.translate((x, y, 0))
original_case = original_case.union(post)
cq.exporters.export(original_case, "Q3-Case-Modified.step")
print("Modified case exported successfully")
Creating Compatible Accessory Plate
import cadquery as cq
case = cq.importers.importStep("Q-Series/Q1/Q1-Case.stp")
bbox = case.val().BoundingBox()
plate_thickness = 1.5
margin = 2.0
accessory_plate = (
cq.Workplane("XY")
.box(bbox.xlen - margin * 2, bbox.ylen - margin * 2, plate_thickness)
.faces(">Z")
.workplane()
.pushPoints([(0, 0), (60, 0), (-60, 0)])
.circle(2.5)
.cutThruAll()
)
cq.exporters.export(accessory_plate, "Q1-Top-Plate-Accessory.step")
print(f"Accessory plate: {bbox.xlen:.1f} x {bbox.ylen:.1f} x {plate_thickness}mm")
3D Printing Workflow
Preparing Case for 3D Printing
import cadquery as cq
from cadquery import exporters
case = cq.importers.importStep("K-Max-Series/K3-Max/K3-Max-Case.stp")
case_oriented = case.rotate((0, 0, 0), (1, 0, 0), 180)
exporters.export(case_oriented, "K3-Max-Case-Printable.stl")
bbox = case_oriented.val().BoundingBox()
print(f"Print bed requirements: {bbox.xlen:.1f} x {bbox.ylen:.1f} mm")
print(f"Print height: {bbox.zlen:.1f} mm")
Generating Mounting Bracket
import cadquery as cq
bracket = (
cq.Workplane("XY")
.box(30, 15, 3)
.faces(">Z").workplane()
.rect(20, 8).cutBlind(-1.5)
.faces(">Z").workplane()
.pushPoints([(10, 0), (-10, 0)])
.circle(1.5)
.cutThruAll()
)
cq.exporters.export(bracket, "keychron-mount-bracket.stl", tolerance=0.01)
Common Patterns
Measuring Switch Plate Hole Spacing
import ezdxf
import math
doc = ezdxf.readfile("Q-Series/Q6/Q6-Plate.dxf")
msp = doc.modelspace()
switch_holes = [c for c in msp.query('CIRCLE') if 13 < c.dxf.radius * 2 < 15]
centers = [(c.dxf.center.x, c.dxf.center.y) for c in switch_holes]
centers.sort(key=lambda p: (p[1], p[0]))
for i in range(len(centers) - 1):
dx = centers[i+1][0] - centers[i][0]
dy = centers[i+1][1] - centers[i][1]
distance = math.sqrt(dx**2 + dy**2)
if distance < 25:
print(f"Switch spacing: {distance:.2f}mm")
Extracting Stabilizer Positions
import cadquery as cq
import json
keyboard = cq.importers.importStep("Q-Pro-Series/Q5-Pro/Q5-Pro-Full-Model.stp")
def find_stabilizer_cutouts(assembly):
"""Extract stabilizer mounting positions from assembly"""
positions = []
for solid in assembly.solids().vals():
bbox = solid.BoundingBox()
if 6 < bbox.xlen < 8 and bbox.zlen < 5:
positions.append({
"x": round((bbox.xmin + bbox.xmax) / 2, 2),
"y": round((bbox.ymin + bbox.ymax) / 2, 2),
"width": round(bbox.xlen, 2)
})
return positions
stabilizers = find_stabilizer_cutouts(keyboard)
print(json.dumps(stabilizers, indent=2))
Creating Custom Keycap Profile
import cadquery as cq
cherry_ref = cq.importers.importStep("Keycap Profiles/Cherry Profile/Cherry-R1.stp")
ref_bbox = cherry_ref.val().BoundingBox()
def create_custom_keycap(top_width=12, top_depth=12, height=8, angle=7):
"""Generate custom keycap with specified parameters"""
keycap = (
cq.Workplane("XY")
.box(18, 18, 1)
.faces(">Z").workplane()
.box(top_width, top_depth, height)
.faces(">Z").workplane()
.transformed(rotate=(angle, 0, 0))
.circle(top_width / 2).cutBlind(-0.5)
.faces("<Z").workplane()
.rect(4, 1.2).extrude(4)
.rect(1.2, 4).cutThruAll()
)
return keycap
custom_cap = create_custom_keycap(top_width=13, top_depth=13, height=9)
cq.exporters.export(custom_cap, "custom-keycap.step")
Batch Processing Multiple Models
import os
import cadquery as cq
from pathlib import Path
q_series_path = Path("Q-Series")
output_dir = Path("exports/q-series-stl")
output_dir.mkdir(parents=True, exist_ok=True)
for model_dir in q_series_path.iterdir():
if not model_dir.is_dir():
continue
case_files = list(model_dir.glob("*Case*.stp")) + list(model_dir.glob("*Case*.step"))
for case_file in case_files:
try:
print(f"Processing {case_file.name}...")
case = cq.importers.importStep(str(case_file))
output_file = output_dir / f"{case_file.stem}.stl"
cq.exporters.export(case, str(output_file))
print(f" → Exported to {output_file.name}")
except Exception as e:
print(f" ✗ Error processing {case_file.name}: {e}")
Configuration and Environment
Setting Up CAD Toolchain
pip install cadquery ezdxf numpy
pip install OCP
pip install freecad
Environment Variables
import os
from pathlib import Path
KEYCHRON_REPO = Path(os.getenv("KEYCHRON_REPO_PATH", "./Keychron-Keyboards-Hardware-Design"))
EXPORT_TOLERANCE = float(os.getenv("CAD_EXPORT_TOLERANCE", "0.01"))
MESH_RESOLUTION = float(os.getenv("STL_MESH_RESOLUTION", "0.1"))
PRINT_LAYER_HEIGHT = float(os.getenv("PRINT_LAYER_HEIGHT", "0.2"))
PRINT_INFILL = int(os.getenv("PRINT_INFILL_PERCENT", "20"))
License and Usage Guidelines
Understanding the License
This repository is source-available with specific terms:
- ✅ Allowed: Personal use, educational use, studying designs
- ✅ Allowed: Original compatible accessories and add-ons (not subject to commercial restriction)
- ✅ Allowed: Modifications for personal builds
- ❌ Not allowed: Copying and selling Keychron keyboards or mice
- ❌ Not allowed: Using Keychron trademarks as your own branding
Contribution Guidelines
git clone https://github.com/YOUR_USERNAME/Keychron-Keyboards-Hardware-Design.git
cd Keychron-Keyboards-Hardware-Design
git checkout -b fix/q1-plate-dimensions
git add Q-Series/Q1/Q1-Plate.dxf
git commit -m "Fix Q1 plate switch hole spacing to 19.05mm standard"
git push origin fix/q1-plate-dimensions
Troubleshooting
STEP File Won't Open
import OCP
from OCP.STEPControl import STEPControl_Reader
from OCP.IFSelect import IFSelect_RetDone
def validate_step_file(filepath):
"""Check if STEP file is valid and report issues"""
reader = STEPControl_Reader()
status = reader.ReadFile(str(filepath))
if status == IFSelect_RetDone:
print(f"✓ {filepath} is valid STEP file")
reader.TransferRoots()
shape = reader.OneShape()
print(f" Contains {shape.NbChildren()} root shapes")
return True
else:
print(f"✗ {filepath} has errors (status code: {status})")
return False
from pathlib import Path
for step_file in Path("Q-Series/Q1").glob("*.stp"):
validate_step_file(step_file)
Dimension Mismatches
import cadquery as cq
def compare_plate_case_fit(plate_path, case_path):
"""Verify plate fits within case boundaries"""
plate = cq.importers.importStep(plate_path)
case = cq.importers.importStep(case_path)
plate_bbox = plate.val().BoundingBox()
case_bbox = case.val().BoundingBox()
clearance_x = case_bbox.xlen - plate_bbox.xlen
clearance_y = case_bbox.ylen - plate_bbox.ylen
print(f"Plate: {plate_bbox.xlen:.2f} x {plate_bbox.ylen:.2f} mm")
print(f"Case: {case_bbox.xlen:.2f} x {case_bbox.ylen:.2f} mm")
print(f"Clearance: {clearance_x:.2f} x {clearance_y:.2f} mm")
if clearance_x < 0 or clearance_y < 0:
print("⚠ WARNING: Plate larger than case!")
return False
elif clearance_x < 1 or clearance_y < 1:
print("⚠ WARNING: Very tight fit (<1mm clearance)")
else:
print("✓ Dimensions compatible")
return True
compare_plate_case_fit(
,
)
Large File Handling
git lfs install
git lfs track "*.stp"
git lfs track "*.step"
git lfs track "*.dwg"
git lfs clone https://github.com/Keychron/Keychron-Keyboards-Hardware-Design.git
Resources