Compare commits

..

No commits in common. "e18298af463a65604abdcf93cd7e3cbf5385bd98" and "183857b2fff06a2e9abe314be80627a2d9203dff" have entirely different histories.

24 changed files with 401 additions and 646 deletions

2
.gitignore vendored
View File

@ -24,7 +24,7 @@ dist
/ergogen/output /ergogen/output
# Kicad temporary files # Kicad temporary files
/kicad/*/*-backups/ /kicad/*-backups/
*.bak *.bak
*.kicad_pcb-bak *.kicad_pcb-bak
*.kicad_sch-bak *.kicad_sch-bak

View File

@ -5,19 +5,10 @@ Custom ergonomic mechanical keyboard with low-profile (Choc) switches.
## Versions ## Versions
- **eepyBoard v1.0: Hardware revision 1** - **eepyBoard v1.0: Hardware revision 1**
- Mostly ortholinear layout with 13 columns (73 keys + rotary encoder) - Mostly ortholinear layout with 13 columns
- Bottom row: two regular keys + four thumb keys (per side, symmetrical) - Bottom row: two regular keys + four thumb keys (per side, symmetrical)
- Partial F-key row (Esc, F1-F8) and rotary encoder - Partial F-key row (Esc, F1-F8) and rotary encoder
- RP2040 controller board in upper right corner - RP2040 controller board in upper right corner
- **eepyBoard v1.1: Hardware revision 2**
- Column staggered split layout (single board) with a total of 15 columns and 6 rows (88 keys + 2 rotary encoders)
- Each side has 7 columns by 5 rows (not counting function keys and arrow keys)
- Bottom row: three regular keys + four thumb keys on each side, and an additional centered thumb key
- Full F-key row (Esc, F1-F12)
- 4 arrow keys in the center
- 2 pressable rotary encoders (one in the center of the keyboard, one in the upper right)
- Compact RP2040 "Pico Mini" controller board in the center
## Repository structure ## Repository structure
@ -45,35 +36,31 @@ Copy the file to `kicad/eepyboard.kicad_pcb`.
Open the file in KiCad (create a project if non exists yet). Finalize the PCB in KiCad. Open the file in KiCad (create a project if non exists yet). Finalize the PCB in KiCad.
1. Round off the bottom corners of the board outline: 1. Run the Design Rules Checker. Check the errors. Most of them can be ignored/excluded.
- Remove the bottom center line and the two small fillet arcs next to it.
- Select the two remaining bottom lines. Right click them and use "Shape Modification -> Extend Lines to Meet".
- Right click the two angled lines again. Use "Shape Modification -> Fillet Lines..." with a 200mm radius.
2. Run the Design Rules Checker. Check the errors. Most of them can be ignored/excluded.
- All "Footprint not found in libraries" can be ignored completely. This is due to how Ergogen generates the PCB. - All "Footprint not found in libraries" can be ignored completely. This is due to how Ergogen generates the PCB.
3. Add VCC and GND planes. 2. Add VCC and GND planes.
- Menu: `File -> Board Setup` - Menu: `File -> Board Setup`
- On "Physical Stackup", change the copper layer number to 4. - On "Physical Stackup", change the copper layer number to 4.
- On "Board Editor Layers", change the type of `In1.Cu` and `In2.Cu` to "power plane". - On "Board Editor Layers", change the type of `In1.Cu` and `In2.Cu` to "power plane".
- NOTE: `In1.Cu` will be the VCC plane, `In2.Cu` will be a GND plane. - NOTE: `In1.Cu` will be the VCC plane, `In2.Cu` will be a GND plane.
4. Add filled zones to the VCC and GND planes. 3. Add filled zones to the VCC and GND planes.
- Select the `In1.Cu` layer. - Select the `In1.Cu` layer.
- Use the "Add a filled zone" tool and draw a rectangle that contains the entire board. Assign the zone to VCC. - Use the "Add a filled zone" tool and draw a rectangle that contains the entire board. Assign the zone to VCC.
- Repeat the same process for the `In2.Cu` layer and assign the zone to GND. - Repeat the same process for the `In2.Cu` layer and assign the zone to GND.
- The zones don't need to be filled just yet. - The zones don't need to be filled just yet.
5. Add another filled zone on the `B.Cu` layer and assign it to GND. 4. Add another filled zone on the `B.Cu` layer and assign it to GND.
6. Route all signal traces (no VCC or GND yet). Recommended order: 5. Route all signal traces (no VCC or GND yet). Recommended order:
- Matrix rows (on `B.Cu`) - Matrix rows (on `B.Cu`)
- Matrix columns (with vias on `F.Cu`) - Matrix columns (with vias on `F.Cu`)
- NeoPixel data pins - NeoPixel data pins
- Connect everything to the MCU. - Connect everything to the MCU.
7. Route VCC traces. 6. Route VCC traces.
- Connect the VCC traces between the NeoPixel chips and the capacitors with a 0.750 mm track. - Connect the VCC traces between the NeoPixel chips and the capacitors with a 0.750 mm track.
- Place free-standing vias (Ctrl+Shift+V) in the middle of the just created VCC traces. - Place free-standing vias (Ctrl+Shift+V) in the middle of the just created VCC traces.
8. Route GND traces. 7. Route GND traces.
- Draw short GND traces with a 0.750 mm track and a via at the end next to the GND pads of the NeoPixel chips. - Draw short GND traces with a 0.750 mm track and a via at the end next to the GND pads of the NeoPixel chips.
9. Fill all zones by pressing B. Make sure that all nets are routed. 8. Fill all zones by pressing B. Make sure that all nets are routed.
10. Run the Design Rules Checker and make sure there are no (relevant) violations. 9. Run the Design Rules Checker and make sure there are no (relevant) violations.
#### Export Gerber files #### Export Gerber files

View File

@ -8,7 +8,7 @@ meta:
# Required version of Ergogen # Required version of Ergogen
engine: "4.1.0" engine: "4.1.0"
# Version of the board # Version of the board
version: "1.1" version: "1.0"
author: binaryDiv author: binaryDiv
# Define units and default values used throughout this file # Define units and default values used throughout this file
@ -29,409 +29,268 @@ units:
$default_spread: cx $default_spread: cx
$default_padding: cy $default_padding: cy
# Vertical offset between function key row and main zone # Vertical offset between function keys and primary keys
function_offset: 3 function_zone_offset: 3
# Horizontal offset/gap between function key groups (Esc, F1-F4, F5-F8, F9-F12) # Define the points (i.e. keys)
function_gap: 6
# Distance between left and right half of the main zone
split_gap_width: 2cx + 3
# Radius of rotary encoder knob (just for visualization)
rotary_encoder_radius: 10
# Define the points (primarily the keys, but also some auxiliary points for
# placement of other components, e.g. rotary encoders and the controller board)
points: points:
key: key:
# Tag all points as "is_key" unless overwritten # Tag all points as "is_key" unless overwritten
tags: [ is_key ] tags: [ is_key ]
# Use 5V as VCC for all RGB LEDs, except for the first one in the chain
led_vcc_net: VCC
zones: zones:
# Main keyboard zone # Primary keyboard zone
main: primary:
# Set anchor to fix placement on KiCad sheet # Set anchor to fix placement on KiCad sheet
anchor: anchor:
shift: [ 75, -175 ] shift: [ 100, -150 ]
# Key rows (from bottom to top) # Key rows (from bottom to top)
rows: rows:
# Modifier row (Ctrl etc., but excluding the thumb keys) # Modifier row (Ctrl, ..., but excluding the thumb keys)
mods: mods:
row_net: ROW1 row_net: GP17
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
tags: [ is_key, flip_led ]
# Bottom letter row # Bottom letter row (Shift, ZXCV...)
bottom: bottom:
row_net: ROW2 row_net: GP16
led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}" led_previous_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
tags: [ is_key, flip_led ]
# Home letter row # Middle/home letter row (Caps Lock, ASDFG...)
home: home:
row_net: ROW3 row_net: GP15
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
# Top letter row
top:
row_net: ROW4
led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
tags: [ is_key, flip_led ] tags: [ is_key, flip_led ]
# Number row # Top letter row (Tab, QWERT...)
top:
row_net: GP14
led_previous_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
# Number row (`, 12345...)
numbers: numbers:
row_net: ROW5 row_net: GP13
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
tags: [ is_key, flip_led ]
# Key columns (from left to right) # Key columns (from left to right)
columns: columns:
# Left-most column (extra keys) # Left-most column (`, Tab, Caps Lock, Shift, Ctrl)
left:
key.column_net: COL1
key.neighbor_col_right: zero
rows.numbers.led_next_key: main_left_top
rows.home.led_next_key: main_left_bottom
# Use placeholder to mark end of the RGB LED chain (unused net name)
rows.mods.led_next_key: END_OF_CHAIN
# Modifier column (`, Tab, Shift, etc.)
zero: zero:
key.stagger: 1/8cy key.column_net: GP8
key.column_net: COL2
key.neighbor_col_left: left
key.neighbor_col_right: one key.neighbor_col_right: one
rows.numbers.led_previous_key: function_esc_default
rows.home.led_previous_key: primary_zero_top
rows.mods.led_previous_key: primary_zero_bottom
# First letter column (1, Q) # First letter column (1, Q)
one: one:
key.stagger: 1/8cy key.column_net: GP7
key.column_net: COL3
key.neighbor_col_left: zero key.neighbor_col_left: zero
key.neighbor_col_right: two key.neighbor_col_right: two
# Second letter column (2, W) # Second letter column (2, W)
two: two:
key.stagger: 1/8cy key.column_net: GP6
key.column_net: COL4
key.neighbor_col_left: one key.neighbor_col_left: one
key.neighbor_col_right: three key.neighbor_col_right: three
rows.mods.skip: true rows.mods.skip: true
# Third letter column (3, E) # Third letter column (3, E)
three: three:
key.column_net: COL5 key.column_net: GP5
key.neighbor_col_left: two key.neighbor_col_left: two
key.neighbor_col_right: four key.neighbor_col_right: four
rows.mods.skip: true rows.mods.skip: true
# Fourth letter column (4, R) # Fourth letter column (4, R)
four: four:
key.stagger: -(1/4cy) key.column_net: GP4
key.column_net: COL6
key.neighbor_col_left: three key.neighbor_col_left: three
key.neighbor_col_right: five key.neighbor_col_right: five
rows.mods.skip: true rows.mods.skip: true
# Fifth letter column (5, T) # Fifth letter column (5, T)
five: five:
key.stagger: -(1/4cy) key.column_net: GP3
key.column_net: COL7
key.neighbor_col_left: four key.neighbor_col_left: four
key.neighbor_col_right: six key.neighbor_col_right: six
rows.top.led_next_key: arrow_keys_up
rows.bottom.led_next_key: arrow_keys_down
rows.mods.skip: true rows.mods.skip: true
# (Gap with arrow keys in the middle: Matrix column 8)
# Sixth letter column (6, Y) # Sixth letter column (6, Y)
six: six:
key.spread: cx + split_gap_width key.column_net: GP2
key.column_net: COL9
key.neighbor_col_left: five key.neighbor_col_left: five
key.neighbor_col_right: seven key.neighbor_col_right: seven
rows.home.led_next_key: arrow_keys_right
rows.mods.skip: true rows.mods.skip: true
# Seventh letter column (7, U) # Seventh letter column (7, U)
seven: seven:
key.stagger: 1/4cy key.column_net: GP1
key.column_net: COL10
key.neighbor_col_left: six key.neighbor_col_left: six
key.neighbor_col_right: eight key.neighbor_col_right: eight
rows.mods.skip: true rows.mods.skip: true
# Eighth letter column (8, I) # Eighth letter column (8, I)
eight: eight:
key.stagger: 1/4cy key.column_net: GP0
key.column_net: COL11
key.neighbor_col_left: seven key.neighbor_col_left: seven
key.neighbor_col_right: nine key.neighbor_col_right: nine
rows.mods.skip: true rows.mods.skip: true
# Nineth letter column (9, O) # Nineth letter column (9, O)
nine: nine:
key.column_net: COL12 key.column_net: GP25
key.neighbor_col_left: eight key.neighbor_col_left: eight
key.neighbor_col_right: ten key.neighbor_col_right: ten
rows.mods.skip: true rows.mods.skip: true
# Tenth letter column (0, P) # Tenth letter column (0, P)
ten: ten:
key.stagger: -(1/8cy) key.column_net: GP24
key.column_net: COL13
key.neighbor_col_left: nine key.neighbor_col_left: nine
key.neighbor_col_right: eleven key.neighbor_col_right: eleven
rows.mods.led_next_key: mirror_thumb_one rows.mods.skip: true
# Eleventh letter column (-, [) # Eleventh letter column (-, [)
eleven: eleven:
key.stagger: -(1/8cy) key.column_net: GP23
key.column_net: COL14
key.neighbor_col_left: ten key.neighbor_col_left: ten
key.neighbor_col_right: twelve key.neighbor_col_right: twelve
rows.mods.led_previous_key: thumb_mirror_one_default
# Twelfth letter column (=, ]) # Twelfth letter column (=, ])
twelve: twelve:
key.stagger: -(1/8cy) key.column_net: GP22
key.column_net: COL15
key.neighbor_col_left: eleven key.neighbor_col_left: eleven
rows.top.led_next_key: main_twelve_home rows.top.led_previous_key: primary_twelve_numbers
rows.bottom.led_next_key: main_twelve_mods rows.bottom.led_previous_key: primary_twelve_home
# Thumb fan # Thumb fan
thumb: thumb:
# Position thumb keys based on the third key in the modifier row # Position thumb keys based on the (non-existant) third key in the modifier row
anchor: anchor:
ref: main_one_mods ref: primary_two_mods
shift: [ cx + 1, 0 ] shift: [ 2, 0 ]
# Zone-wide key settings # Rotate the thumb keys around the bottom-left corner of the key
key: key:
row_net: ROW1 row_net: GP17
# Rotate the thumb keys around the bottom-left corner of the key tags: [ is_key, flip_led ]
origin: [ -0.5cx, -0.5cy ] origin: [ -0.5cx, -0.5cy ]
splay: -6 splay: -4
# Define thumb keys # Define thumb keys
columns: columns:
one: one:
key.column_net: COL4 key.column_net: GP6
key.mirror.column_net: COL12 key.mirror.column_net: GP24
key.led_next_key: main_one_mods key.led_previous_key: primary_one_mods
key.mirror.led_next_key: mirror_thumb_two key.mirror.led_previous_key: thumb_mirror_two_default
two: two:
key.column_net: COL5 key.column_net: GP5
key.mirror.column_net: COL11 key.mirror.column_net: GP25
key.led_next_key: thumb_one key.led_previous_key: thumb_one_default
key.mirror.led_next_key: mirror_thumb_three key.mirror.led_previous_key: thumb_mirror_three_default
three: three:
key.column_net: COL6 key.column_net: GP4
key.mirror.column_net: COL10 key.mirror.column_net: GP0
key.led_next_key: thumb_two key.led_previous_key: thumb_two_default
key.mirror.led_next_key: mirror_thumb_four key.mirror.led_previous_key: thumb_mirror_four_default
four: four:
key.column_net: COL7 key.column_net: GP3
key.mirror.column_net: COL9 key.mirror.column_net: GP1
key.led_next_key: thumb_three key.led_previous_key: thumb_three_default
key.mirror.led_next_key: thumb_center key.mirror.led_previous_key: thumb_four_default
# Mirror the thumb fan # Mirror the thumb fan
mirror: mirror:
ref: main_five_bottom ref: primary_five_numbers
shift: [ 0.5cx, 0.5cy ] shift: [ 0.5cx, 0.5cx ]
distance: split_gap_width distance: 1cx
# Single key centered between thumb keys # Function key row above the primary zone (Esc + 8 more keys)
thumb_center: # (Note that the keys are only named "f[1-8]" for convenience and are not a full set of F1-F12 keys. They can be
anchor: # used as F-keys when needed, but will more likely be used for macros and other special functions.)
# Center between the two inner thumb keys
aggregate.parts:
- thumb_four
- mirror_thumb_four
shift: [ 0, -0.25cy ]
# Key settings (instead of rows/columns)
key:
width: 1.5kcx
row_net: ROW1
column_net: COL8
led_next_key: thumb_four
tags: [ is_key, 1_5u ]
# Arrow keys in the middle of the keyboard
# In the matrix, these keys act as 4 keys in a single column (column 8, row 2 to 5)
arrow_keys:
anchor:
# Center between the V and B keys, then shift down half a key
aggregate.parts:
- main_five_bottom
- main_six_bottom
shift: [ 0, -cy/2 ]
# Assign all keys to the same column
key:
column_net: COL8
rows:
# Bottom row: Only the down key
bottom:
row_net: ROW2
tags: [ is_key, flip_led ]
# Center row: Left and right keys (shifted to the left to center them)
center:
adjust.shift: [ -cx/2, 0 ]
# Use ROW3 for the left key and ROW5 for the right key (overridden in columns)
row_net: ROW3
# Top row: Only the up key
top:
row_net: ROW4
tags: [ is_key, flip_led ]
columns:
# Left column: Up, left, down keys
left:
rows.top:
name: arrow_keys_up
led_next_key: main_six_top
rows.center:
name: arrow_keys_left
led_next_key: main_five_home
rows.bottom:
name: arrow_keys_down
led_next_key: main_six_bottom
# Right column: Only right key (assigned to matrix row 5)
right:
rows.top.skip: true
rows.center:
name: arrow_keys_right
row_net: ROW5
led_next_key: arrow_keys_left
rows.bottom.skip: true
# Function key row above the main zone (Esc, F1-F12)
function: function:
# Position function key row above main zone with 3mm of vertical offset # Position function key row above primary zone with 3mm of vertical offset
anchor: anchor:
# To center the F keys on the top of the keyboard: ref: primary_zero_numbers
# - Get center point between keys 3 and 8 (they have the highest staggering) shift: [ 0, cy + function_zone_offset ]
# - Shift upwards by one key + an offset to have a small gap
# - Shift to the left by 7 keys (Esc, F1-F6) + the gaps between the F key groups.
aggregate.parts:
- main_three_numbers
- main_eight_numbers
shift: [ -6.5cx - 2 * function_gap, cy + function_offset ]
# Single row zone: Assign row net
key: key:
row_net: ROW6 row_net: GP12
tags: [ is_key, flip_led ]
# Define function keys # Define function keys
columns: columns:
# Escape key # Escape key
esc: esc:
key.shift: [ 0, -cy/8 ] key.column_net: GP8
key.column_net: COL1 key.led_previous_key: function_f1_default
key.led_next_key: function_f1
# Esc is the first key in the RGB LED chain.
# The first LED needs to run on a lower voltage (see explanation below).
key.led_vcc_net: VCC_first_led
# Block of 4 keys with gap # Block of 4 keys with 6mm offset
f1: f1:
key.spread: cx + function_gap key.spread: cx + 6
key.column_net: COL2 key.column_net: GP7
key.led_next_key: function_f2 key.led_previous_key: function_f2_default
f2: f2:
key.column_net: COL3 key.column_net: GP6
key.led_next_key: function_f3 key.led_previous_key: function_f3_default
f3: f3:
key.column_net: COL4 key.column_net: GP5
key.led_next_key: function_f4 key.led_previous_key: function_f4_default
f4: f4:
key.column_net: COL5 key.column_net: GP4
key.led_next_key: function_f5 key.led_previous_key: function_f5_default
# Block of 4 keys with gap # Block of 4 keys with 6mm offset
f5: f5:
key.spread: cx + function_gap key.spread: cx + 6
key.column_net: COL6 key.column_net: GP3
key.led_next_key: function_f6 key.led_previous_key: function_f6_default
f6: f6:
key.column_net: COL7 key.column_net: GP2
key.led_next_key: function_f7 key.led_previous_key: function_f7_default
# Skip matrix column 8 (arrow keys)
f7: f7:
key.column_net: COL9 key.column_net: GP1
key.led_next_key: function_f8 key.led_previous_key: function_f8_default
f8: f8:
key.column_net: COL10 key.column_net: GP0
key.led_next_key: function_f9 key.led_previous_key: status_led
# Block of 4 keys with gap # Additional points for special components in the upper right corner: Status LED, reset button, rotary encoder
f9: special:
key.spread: cx + function_gap # Align points with the function key zone
key.column_net: COL11
key.led_next_key: function_f10
f10:
key.column_net: COL12
key.led_next_key: function_f11
f11:
key.column_net: COL13
key.led_next_key: function_f12
f12:
key.column_net: COL14
key.led_next_key: main_twelve_numbers
# RP2040 Pico Mini controller board
controller:
anchor: anchor:
# Place in the center of the keyboard, above the arrow keys ref: primary_ten_numbers
aggregate.parts: shift: [ 0, cy + function_zone_offset ]
- main_five_numbers
- main_six_numbers
shift: [ 0, 1.5 ]
# This is an auxiliary point to place the controller, not an actual key # These are not actually keys
key: key:
tags: [ is_controller ] tags: [ no_key ]
width: 18
height: 36
# Rotary encoder in the center of the keyboard (on top of the controller board) # Define points
rotary_center: columns:
anchor: # Neopixel status LED (first in the chain, see comment in PCB footprints for details)
ref: controller status_led:
rotate: -90 key:
key: width: 3.4
tags: [ is_rotary_encoder ] height: 3.0
width: 2 * rotary_encoder_radius
height: 2 * rotary_encoder_radius
# Matrix position of the switch: between F7 and F8
row_net: ROW6
column_net: COL8
# Rotary encoder in the top right corner of the keyboard # Reset button
rotary_top_right: reset_button:
anchor: key:
ref: function_f12 shift: [ -0.25cx - 1, 0 ]
shift: [ cx + 2 + function_gap, -cy/8 ] width: 6.5
key: height: 6.5
tags: [ is_rotary_encoder ]
width: 2 * rotary_encoder_radius # Rotary encoder in the upper right corner
height: 2 * rotary_encoder_radius rotary:
# Matrix position of the switch: Function row, right of F12 key:
row_net: ROW6 row_net: GP12
column_net: COL15 column_net: GP22
shift: [ -0.25cx, 0 ]
width: 20.5
height: 20.5
tags: [ is_rotary_encoder ]
# Generate outlines that can be used in the PCB and for 3D models # Generate outlines that can be used in the PCB and for 3D models
outlines: outlines:
# Outline of the key caps # Outline of the key caps
keys: keys:
# 1u keys
- what: rectangle - what: rectangle
operation: stack where: is_key
where: [ [ is_key, -1_5u ] ]
size: [ kcx, kcy ] size: [ kcx, kcy ]
# 1.5u keys
- what: rectangle
operation: stack
where: [ [ is_key, 1_5u ] ]
size: [ 1.5kcx, kcy ]
# Rotary encoder with knob (2cm) # Rotary encoder with knob (2cm)
rotary_encoder: rotary_encoder:
@ -440,77 +299,46 @@ outlines:
size: [ 14, 14 ] size: [ 14, 14 ]
- what: circle - what: circle
operation: stack operation: stack
radius: rotary_encoder_radius radius: 10
# Outline for the PCB # Outline for the PCB
board_outline: board:
- what: polygon - what: polygon
points: points:
# Top left corner - ref: primary_zero_numbers
- ref: &corner_top_left_anchor shift: [ -0.5cx + 1, 1.5cy + function_zone_offset + 2 ]
- ref: main_left_numbers - ref: primary_twelve_numbers
affect: x shift: [ 0.5cx - 1, 1.5cy + function_zone_offset + 2]
- ref: function_f1 - ref: primary_twelve_mods
affect: y shift: [ 0.5cx - 1, -0.5cy + 2 ]
- shift: [ -0.5kcx, 0.5kcy ] - ref: mirror_thumb_four
# Extend all corners 4mm to the sides to get a 4mm border shift: [ 0.5cx, -0.5cy + 2 ]
shift: [ -4, 4 ] - ref: thumb_four
shift: [ 0.5cx, -0.5cy + 2 ]
# Top right corner - ref: primary_zero_mods
- ref: &corner_top_right_anchor shift: [ -0.5cx + 1, -0.5cy + 2 ]
- ref: main_twelve_numbers expand: 4
affect: x
- ref: function_f12
affect: y
- shift: [ 0.5kcx, 0.5kcy ]
shift: [ 4, 4 ]
# Bottom right corner
- ref:
- ref: main_twelve_mods
shift: [ 0.5kcx, -0.5kcy ]
shift: [ 4, -4 ]
# Bottom center-right corner (below thumb keys)
- ref:
- ref: mirror_thumb_four
shift: [ 0.5kcx, -0.5kcy ]
# Discard the rotation, so the next shift moves the corner straight down
affect: xy
shift: [ 0, -4 ]
# Bottom center-left corner (below thumb keys)
- ref:
- ref: thumb_four
shift: [ 0.5kcx, -0.5kcy ]
# Discard the rotation, so the next shift moves the corner straight down
affect: xy
shift: [ 0, -4 ]
# Bottom left corner
- ref:
- ref: main_left_mods
shift: [ -0.5kcx, -0.5kcy ]
shift: [ -4, -4 ]
# Round off the corners with an 8mm radius fillet.
# NOTE: To get a smoother arc on the bottom of the board, do some post-processing in KiCad:
# 1. Remove the bottom center line and the two small fillet arcs next to it.
# 2. Select the two remaining bottom lines. Right click them and use "Shape Modification -> Extend Lines to Meet".
# 3. Right click the two angled lines again. Use "Shape Modification -> Fillet Lines..." with a 200mm radius.
fillet: 8
# Preview version of board with key caps and components for visualization # Preview version of board with key caps and components for visualization
board_preview: board_preview:
- board_outline - board
- ^keys - ^keys
# RP2040 controller board
# RP2040 Pico Mini controller board
- what: rectangle - what: rectangle
operation: stack operation: stack
where: is_controller where: primary_eleven_numbers
size: [ 18, 36 ] size: [ 54, 20 ]
adjust.shift: [ 0, cy + function_zone_offset ]
# Status LED
- what: rectangle
operation: stack
where: special_status_led
size: [ 3.4, 3.0 ]
# Reset button
- what: rectangle
operation: stack
where: special_reset_button
size: [ 6.5, 6.5 ]
# Rotary encoder # Rotary encoder
- what: outline - what: outline
name: rotary_encoder name: rotary_encoder
@ -520,12 +348,12 @@ outlines:
# Cutouts for the switches # Cutouts for the switches
switch_cutouts: switch_cutouts:
- what: rectangle - what: rectangle
where: is_key where: true
size: 14 size: 14
# Board outline with switch cutouts # Board outline with switch cutouts
switch_plate: switch_plate:
- board_outline - board
- -switch_cutouts - -switch_cutouts
# Generate the PCB # Generate the PCB
@ -534,48 +362,20 @@ pcbs:
# Define outline (edges) of the board based on the outlines defined above # Define outline (edges) of the board based on the outlines defined above
outlines: outlines:
main: main:
outline: board_outline outline: board
# Define PCB components # Define PCB components
footprints: footprints:
controller: controller:
what: rp2040_pico_mini what: rp2040_purple
where: is_controller where:
ref: primary_eleven_numbers
shift: [ 0, cy + function_zone_offset ]
rotate: 90
params: params:
# Mount the controller board on the backside of the PCB
orientation: down orientation: down
# Use 5V from USB as VCC net # Use VBUS (5V from USB) as VCC net
5V: VCC VBUS: VCC
# RGB LEDs: The chain starts with the Esc key
GP29: led_din_function_esc
# Matrix columns
GP27: COL1
GP26: COL2
GP25: COL3
GP23: COL4
GP22: COL5
GP21: COL6
GP20: COL7
GP17: COL8 # Center column (arrow keys)
GP12: COL9
GP11: COL10
GP10: COL11
GP9: COL12
GP8: COL13
GP7: COL14
GP6: COL15
# Matrix rows
GP13: ROW1
GP14: ROW2
GP19: ROW3
GP18: ROW4
GP15: ROW5
GP28: ROW6
# Rotary encoders
GP0: rotary_top_right_a
GP1: rotary_top_right_b
GP3: rotary_center_a
GP2: rotary_center_b
choc_hotswap: choc_hotswap:
what: choc_pretty what: choc_pretty
@ -585,10 +385,10 @@ pcbs:
params: params:
keycaps: true keycaps: true
hotswap: true hotswap: true
from: "diode_{{name}}" from: "{{colrow}}"
to: "{{column_net}}" to: "{{column_net}}"
key_diode: diode:
what: diode_smd what: diode_smd
where: is_key where: is_key
adjust: adjust:
@ -597,21 +397,21 @@ pcbs:
resist: true resist: true
params: params:
side: B side: B
from: "diode_{{name}}" from: "{{colrow}}"
to: "{{row_net}}" to: "{{row_net}}"
rotary_encoder: rotary_encoder:
what: rotary_modified what: rotary_modified
where: is_rotary_encoder where: is_rotary_encoder
params: params:
from: "diode_{{name}}" from: "{{colrow}}"
to: "{{column_net}}" to: "{{column_net}}"
A: "{{name}}_a" A: GP10
B: "{{name}}_b" B: GP11
C: GND C: GND
rotary_encoder_diode: diode_rotary_encoder:
$extends: pcbs.eepyboard.footprints.key_diode $extends: pcbs.eepyboard.footprints.diode
where: is_rotary_encoder where: is_rotary_encoder
adjust: adjust:
shift: [ 0, 0 ] shift: [ 0, 0 ]
@ -626,9 +426,8 @@ pcbs:
shift: [ 0, 4.7 ] shift: [ 0, 4.7 ]
params: params:
side: B side: B
din: "led_din_{{name}}" din: "led_dout_{{led_previous_key}}"
dout: "led_din_{{led_next_key}}" dout: "led_dout_{{zone.name}}_{{colrow}}"
VCC: "{{led_vcc_net}}"
led_capacitor: led_capacitor:
what: cap_0805 what: cap_0805
@ -639,12 +438,12 @@ pcbs:
resist: true resist: true
params: params:
side: B side: B
from: "{{led_vcc_net}}" from: VCC
to: GND to: GND
led_chip_flipped: led_chip_flipped:
$extends: pcbs.eepyboard.footprints.led_chip $extends: pcbs.eepyboard.footprints.led_chip
# Place flipped (rotated by 180°) LEDs in all rows with the flip_led tag # Place flipped (rotated by 180°) LEDs in all rows *with* the flip_led tag
where: [ [ is_key, flip_led ] ] where: [ [ is_key, flip_led ] ]
adjust.rotate: 180 adjust.rotate: 180
@ -656,82 +455,93 @@ pcbs:
rotate: 270 rotate: 270
resist: true resist: true
# The first SK6812 chip in the chain has to run on a reduced voltage to serve as a makeshift level converter for # Additional first LED (not bound to a key) that serves as a status LED.
# all following RGB LEDs. # Additionally, it runs on a reduced voltage to serve as a level converter for the next LED: The RP2040 runs on
# This is needed because the RP2040 runs on 3.3V, but the SK6812 runs on 5V and requires at least 3.5V (5V * 0.7) # 3.3V, but the SK6812 runs on 5V and requires at least 3.5V (5V * 0.7) for the control signal. By reducing the
# for the control signal. By reducing the power voltage with a diode, the threshold is reduced as well, so that # power voltage with a diode, the threshold is reduced as well, so that the signal of the RP2040 is strong enough.
# the signal of the RP2040 is strong enough. status_led_chip:
first_led_voltage_diode: what: sk6812_mini_e
what: diode_smd where: special_status_led
where: function_esc params:
side: B
VCC: VCC_status_led
din: GP9
dout: led_dout_status_led
status_led_capacitor:
what: cap_0805
where: special_status_led
adjust: adjust:
shift: [ 9.5, 2.25 ] shift: [ -6, -0.2 ]
rotate: -90 rotate: 90
params:
side: B
from: VCC_status_led
to: GND
# Diode to reduce the voltage of the first LED chip by ~0.7V
status_led_diode:
what: diode_smd
where: special_status_led
adjust:
shift: [ 0, 3.5 ]
rotate: 0
params: params:
side: B side: B
from: VCC from: VCC
to: VCC_first_led to: VCC_status_led
# Mounting holes: Top left and right corners reset_button:
what: button_6x6
where: special_reset_button
params:
from: GND
to: RUN
# Mounting holes
mounting_hole_top_left: mounting_hole_top_left:
what: mountinghole_m2 what: mountinghole_m2
where: where:
ref: *corner_top_left_anchor ref: function_esc
shift: [ 2, -2 ] shift: [ -9, 11.5 ]
mounting_hole_top_center:
what: mountinghole_m2
where:
ref: function_f4
shift: [ 12.5, 0 ]
mounting_hole_top_right: mounting_hole_top_right:
what: mountinghole_m2 what: mountinghole_m2
where: where:
ref: *corner_top_right_anchor ref: primary_twelve_numbers
shift: [ -2, -2 ] shift: [ 9, cy + function_zone_offset + 11.5 ]
# Mounting holes: Top center, left and right of the controller
mounting_hole_controller_left:
what: mountinghole_m2
where:
ref: main_five_numbers
shift: [ 0, 12 ]
mounting_hole_controller_right:
what: mountinghole_m2
where:
ref: main_six_numbers
shift: [ 0, 12 ]
# Mounting holes: Bottom center, under columns five/six (but above the thumb keys)
mounting_hole_bottom_center_left:
what: mountinghole_m2
where:
ref: main_five_bottom
shift: [ 0, -13 ]
mounting_hole_bottom_center_right:
what: mountinghole_m2
where:
ref: main_six_bottom
shift: [ 0, -13 ]
# Mounting holes: Bottom left and right corners, under the second key columns
mounting_hole_bottom_left: mounting_hole_bottom_left:
what: mountinghole_m2 what: mountinghole_m2
where: where:
ref: main_zero_mods ref: primary_one_mods
shift: [ 0, -13.5 ] shift: [ 10.5, -12 ]
mounting_hole_bottom_center:
what: mountinghole_m2
where:
ref: primary_six_bottom
shift: [ 0, -23 ]
mounting_hole_bottom_right: mounting_hole_bottom_right:
what: mountinghole_m2 what: mountinghole_m2
where: where:
ref: main_eleven_mods ref: primary_eleven_mods
shift: [ 0, -13.5 ] shift: [ -10.5, -12 ]
# Render text with project name and copyright onto the PCB # Render text with project name and copyright onto the PCB
copyright_text: copyright_text:
what: text what: text
where: where:
ref: thumb_one ref: primary_six_bottom
shift: [ 0, -16.5 ] shift: [ 0, -15 ]
rotate: -0.3 # total rotation: -6.3 (including key rotation)
params: params:
text: |- text: |-
eepyBoard v1.1 by binaryDiv eepyBoard v1.0 by binaryDiv
(c) 2024 (MIT License) (c) 2024 (MIT License)

View File

@ -1,182 +0,0 @@
// Pin header footprint for a small RP2040-based controller board from AliExpress called "Pico Mini RP2040".
// It has a similar footprint to the Pro Micro (NOT pin-compatible though!), but has an additional row of pins
// on the short edge of the board (basically forming a "U").
// -------------------------------
// Params
// orientation: if up (default), the controller will be on the front side of the PCB, otherwise on the back side
module.exports = {
params: {
designator: 'MCU',
orientation: 'up',
// Left side of pins (with USB port at the top), top to bottom
GP0: {type: 'net', value: 'GP0'},
GP1: {type: 'net', value: 'GP1'},
GP2: {type: 'net', value: 'GP2'},
GP3: {type: 'net', value: 'GP3'},
GP4: {type: 'net', value: 'GP4'},
GP5: {type: 'net', value: 'GP5'},
GP6: {type: 'net', value: 'GP6'},
GP7: {type: 'net', value: 'GP7'},
GP8: {type: 'net', value: 'GP8'},
GP9: {type: 'net', value: 'GP9'},
GP10: {type: 'net', value: 'GP10'},
GP11: {type: 'net', value: 'GP11'},
GP12: {type: 'net', value: 'GP12'},
GP13: {type: 'net', value: 'GP13'},
// Bottom side of pins, left to right (i.e. between GP13 and GP19)
GP14: {type: 'net', value: 'GP14'},
GP15: {type: 'net', value: 'GP15'},
GP16: {type: 'net', value: 'GP16'},
GP17: {type: 'net', value: 'GP17'},
GP18: {type: 'net', value: 'GP18'},
// Right side of pins (with USB port at the top), top to bottom
'5V': {type: 'net', value: '5V'}, // 5V USB voltage
GND: {type: 'net', value: 'GND'},
RST: {type: 'net', value: 'RST'}, // Reset pin: Pull down to reset
'3V3': {type: 'net', value: '3V3'}, // 3.3V voltage used by the MCU
GP29: {type: 'net', value: 'GP29'}, // also: ADC4
GP28: {type: 'net', value: 'GP28'}, // also: ADC2
GP27: {type: 'net', value: 'GP27'}, // also: ADC1
GP26: {type: 'net', value: 'GP26'}, // also: ADC0
GP25: {type: 'net', value: 'GP25'},
GP23: {type: 'net', value: 'GP23'},
GP22: {type: 'net', value: 'GP22'},
GP21: {type: 'net', value: 'GP21'},
GP20: {type: 'net', value: 'GP20'},
GP19: {type: 'net', value: 'GP19'},
},
body: p => {
let sign, side, mirror;
if (p.orientation === 'down') {
sign = -1;
side = 'B';
mirror = 'mirror';
} else {
sign = 1;
side = 'F';
mirror = '';
}
const pins_left = [
'GP0', 'GP1', 'GP2', 'GP3', 'GP4', 'GP5', 'GP6', 'GP7', 'GP8', 'GP9', 'GP10', 'GP11', 'GP12', 'GP13',
];
const pins_bottom = [
'GP14', 'GP15', 'GP16', 'GP17', 'GP18',
];
const pins_right = [
'5V', 'GND', 'RST', '3V3', 'GP29', 'GP28', 'GP27', 'GP26', 'GP25', 'GP23', 'GP22', 'GP21', 'GP20', 'GP19',
];
// Functions to map coordinates from a "pin grid" to actual X/Y coordinates.
// The grid is defined as follows (assuming the board is in "up" orientation with the USB port on top):
// - The X coordinates go from 0 (left) to 6 (right)
// - The Y coordinates go from 0 (top) to 13 (bottom)
// This means the pin rows are on these coordinates:
// - Left side of pins: 0/0 to 0/13
// - Right side of pins: 6/0 to 6/13
// - Additional pins on the bottom (not counting the left and right pins): 1/13 to 5/13
// These grid coordinates have to be mapped to footprint coordinates so that the board is centered, meaning
// the grid position 3/6.5 needs to be mapped to footprint coordinates 0/0. The grid is a standard 2.54mm grid.
function pin_pos_x(pin_number) {
// Use integers to avoid floating point issues
return sign * (254 * (pin_number - 3)) / 100;
}
function pin_pos_y(pin_number) {
// Use integers to avoid floating point issues
return (254 * (pin_number - 7) + 127) / 100;
}
function pin_label(pin_name, pos_x, pos_y, pin_side) {
let offset_x = 0;
let offset_y = 0;
let offset_rotation = 0;
let justify = 'left';
if (pin_side === 'left') {
offset_x = -1.3 * sign;
justify = 'right';
} else if (pin_side === 'right') {
offset_x = 1.3 * sign;
} else if (pin_side === 'bottom') {
offset_y = 1.7;
offset_rotation = -90 * sign;
}
// Shorten the name of the "GPxx" pins to save a bit of space on the PCB
if (pin_name.startsWith('GP')) {
pin_name = pin_name.slice(2);
}
return `
(fp_text user ${pin_name}
(at ${pin_pos_x(pos_x) + offset_x} ${pin_pos_y(pos_y) + offset_y} ${p.r + offset_rotation})
(unlocked yes) (layer ${side}.SilkS)
(effects (font (size 0.8 0.8) (thickness 0.15)) (justify ${justify} ${mirror}))
)`;
}
function pin_pad(pad_number, pos_x, pos_y, pin_name) {
return `
(pad ${pad_number} thru_hole circle
(at ${pin_pos_x(pos_x)} ${pin_pos_y(pos_y)} 0)
(size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.Mask)
${p[pin_name]}
)`;
}
function render_pins() {
let pin_label_body = '';
let pin_pad_body = '';
let pad_number = 1;
for (let i = 0; i < pins_left.length; i++) {
pin_label_body += pin_label(pins_left[i], 0, i, 'left');
pin_pad_body += pin_pad(pad_number++, 0, i, pins_left[i]);
}
for (let i = 0; i < pins_bottom.length; i++) {
pin_label_body += pin_label(pins_bottom[i], 1 + i, 13, 'bottom');
pin_pad_body += pin_pad(pad_number++, 1 + i, 13, pins_bottom[i]);
}
for (let i = 0; i < pins_right.length; i++) {
pin_label_body += pin_label(pins_right[i], 6, i, 'right');
pin_pad_body += pin_pad(pad_number++, 6, i, pins_right[i]);
}
return pin_pad_body + '\n' + pin_label_body;
}
return `
(module RP2040_PICO_MINI (layer F.Cu) (tedit 66FD3C6D)
${p.at /* parametric position */}
${'' /* footprint reference */}
(fp_text reference "${p.ref}" (at 0 0) (layer ${side}.SilkS) ${p.ref_hide}
(effects (font (size 1.27 1.27) (thickness 0.15)))
)
(fp_text value "" (at 0 0) (layer ${side}.SilkS) hide
(effects (font (size 1.27 1.27) (thickness 0.15)))
)
${'' /* component outline */}
(fp_line (start -8.8 -18.0) (end 8.8 -18.0) (layer ${side}.SilkS) (width 0.15))
(fp_line (start 8.8 -18.0) (end 8.8 18.0) (layer ${side}.SilkS) (width 0.15))
(fp_line (start 8.8 18.0) (end -8.8 18.0) (layer ${side}.SilkS) (width 0.15))
(fp_line (start -8.8 18.0) (end -8.8 -18.0) (layer ${side}.SilkS) (width 0.15))
${'' /* illustration of the USB port overhang */}
(fp_line (start -4.3 -18.8) (end 4.3 -18.8) (layer Dwgs.User) (width 0.15))
(fp_line (start 4.3 -18.8) (end 4.3 -11.8) (layer Dwgs.User) (width 0.15))
(fp_line (start 4.3 -11.8) (end -4.3 -11.8) (layer Dwgs.User) (width 0.15))
(fp_line (start -4.3 -11.8) (end -4.3 -18.8) (layer Dwgs.User) (width 0.15))
${'' /* pin labels and pads */}
${render_pins()}
)
`;
}
}

View File

@ -0,0 +1,140 @@
// Pin header footprint for a RP2040-based controller board from AliExpress.
// Doesn't really have a name, it's just "the purple one with USB-C".
// The pinout differs from the Raspberry Pi Pico!
// -------------------------------
// Params
// orientation: if up (default), the controller will be on the front side of the PCB, otherwise on the back side
module.exports = {
params: {
designator: 'MCU',
orientation: 'up',
// Right side of pins (with USB port at the top), top to bottom
VBUS: {type: 'net', value: 'VBUS'},
VIN: {type: 'net', value: 'VIN'},
GND: {type: 'net', value: 'GND'},
'3V3_EN': {type: 'net', value: '3V3_EN'},
'3V3': {type: 'net', value: '3V3'},
// (GND pin)
RUN: {type: 'net', value: 'RUN'}, // Reset pin: Pull down to reset
GP29: {type: 'net', value: 'GP29'}, // also: ADC3
GP28: {type: 'net', value: 'GP28'}, // also: ADC2
GP27: {type: 'net', value: 'GP27'}, // also: ADC1
GP26: {type: 'net', value: 'GP26'}, // also: ADC0
AGND: {type: 'net', value: 'AGND'},
GP25: {type: 'net', value: 'GP25'},
GP24: {type: 'net', value: 'GP24'},
GP23: {type: 'net', value: 'GP23'},
GP22: {type: 'net', value: 'GP22'},
GP21: {type: 'net', value: 'GP21'},
GP20: {type: 'net', value: 'GP20'},
GP19: {type: 'net', value: 'GP19'},
GP18: {type: 'net', value: 'GP18'},
// Left side of pins (with USB port at the top), top to bottom
GP0: {type: 'net', value: 'GP0'},
GP1: {type: 'net', value: 'GP1'},
GP2: {type: 'net', value: 'GP2'},
GP3: {type: 'net', value: 'GP3'},
GP4: {type: 'net', value: 'GP4'},
// (GND pin)
GP5: {type: 'net', value: 'GP5'},
GP6: {type: 'net', value: 'GP6'},
GP7: {type: 'net', value: 'GP7'},
GP8: {type: 'net', value: 'GP8'},
GP9: {type: 'net', value: 'GP9'},
GP10: {type: 'net', value: 'GP10'},
GP11: {type: 'net', value: 'GP11'},
GP12: {type: 'net', value: 'GP12'},
// (GND pin)
GP13: {type: 'net', value: 'GP13'},
GP14: {type: 'net', value: 'GP14'},
GP15: {type: 'net', value: 'GP15'},
GP16: {type: 'net', value: 'GP16'},
GP17: {type: 'net', value: 'GP17'},
},
body: p => {
let def_neg, def_pos, side, mirror;
if (p.orientation === 'down') {
def_neg = '';
def_pos = '-';
side = 'B';
mirror = 'mirror';
} else {
def_neg = '-';
def_pos = '';
side = 'F';
mirror = '';
}
const PINS_PER_SIDE = 20;
const PINS_PER_SIDE_HALF = 10;
const pins_left = [
'GP0', 'GP1', 'GP2', 'GP3', 'GP4', 'GND', 'GP5', 'GP6', 'GP7', 'GP8',
'GP9', 'GP10', 'GP11', 'GP12', 'GND', 'GP13', 'GP14', 'GP15', 'GP16', 'GP17',
];
const pins_right = [
'VBUS', 'VIN', 'GND', '3V3_EN', '3V3', 'GND', 'RUN', 'GP29', 'GP28', 'GP27',
'GP26', 'AGND', 'GP25', 'GP24', 'GP23', 'GP22', 'GP21', 'GP20', 'GP19', 'GP18',
];
function pin_pos_y(pin_number) {
// Fucking floating points...
return (254 * (pin_number - PINS_PER_SIDE_HALF) + 127) / 100;
}
function pin_labels(pin_names, right_side) {
const sign = right_side ? def_pos : def_neg;
const justify = right_side ? 'right' : 'left';
let pin_label_body = '';
for (let i = 0; i < PINS_PER_SIDE; i++) {
pin_label_body += `
(fp_text user ${pin_names[i]} (at ${sign}7.62 ${pin_pos_y(i)} ${p.r}) (unlocked yes) (layer ${side}.SilkS)
(effects (font (size 0.8 0.8) (thickness 0.15)) (justify ${justify} ${mirror}))
)`;
}
return pin_label_body;
}
function pin_pads(pin_names, right_side) {
const sign = right_side ? def_pos : def_neg;
const offset = right_side ? 21 : 1;
let pin_pad_body = '';
for (let i = 0; i < PINS_PER_SIDE; i++) {
pin_pad_body += `
(pad ${i + offset} thru_hole circle (at ${sign}8.89 ${pin_pos_y(i)} 0)
(size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.Mask) ${p[pin_names[i]]}
)`;
}
return pin_pad_body;
}
return `
(module RP2040_USB_C_Purple (layer F.Cu) (tedit 66A6C4CD)
${p.at /* parametric position */}
${'' /* footprint reference */}
(fp_text reference "${p.ref}" (at 0 0) (layer ${side}.SilkS) ${p.ref_hide} (effects (font (size 1.27 1.27) (thickness 0.15))))
(fp_text value "" (at 0 0) (layer ${side}.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
${'' /* component outline */}
(fp_line (start -10.16 -28.6) (end 10.16 -28.6) (layer ${side}.SilkS) (width 0.15))
(fp_line (start 10.16 -28.6) (end 10.16 25.4) (layer ${side}.SilkS) (width 0.15))
(fp_line (start 10.16 25.4) (end -10.16 25.4) (layer ${side}.SilkS) (width 0.15))
(fp_line (start -10.16 25.4) (end -10.16 -28.6) (layer ${side}.SilkS) (width 0.15))
${'' /* pin labels */}
${pin_labels(pins_left, false)}
${pin_labels(pins_right, true)}
${'' /* pin pads */}
${pin_pads(pins_left, false)}
${pin_pads(pins_right, true)}
)
`;
}
}