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
# Kicad temporary files
/kicad/*/*-backups/
/kicad/*-backups/
*.bak
*.kicad_pcb-bak
*.kicad_sch-bak

View File

@ -5,20 +5,11 @@ Custom ergonomic mechanical keyboard with low-profile (Choc) switches.
## Versions
- **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)
- Partial F-key row (Esc, F1-F8) and rotary encoder
- 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
- `/layouts`: Keyboard layouts created with the [Keyboard Layout Editor](http://www.keyboard-layout-editor.com)
@ -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.
1. Round off the bottom corners of the board outline:
- 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.
1. 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.
3. Add VCC and GND planes.
2. Add VCC and GND planes.
- Menu: `File -> Board Setup`
- 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".
- 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.
- 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.
- 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.
6. Route all signal traces (no VCC or GND yet). Recommended order:
4. Add another filled zone on the `B.Cu` layer and assign it to GND.
5. Route all signal traces (no VCC or GND yet). Recommended order:
- Matrix rows (on `B.Cu`)
- Matrix columns (with vias on `F.Cu`)
- NeoPixel data pins
- 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.
- 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.
9. 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.
8. Fill all zones by pressing B. Make sure that all nets are routed.
9. Run the Design Rules Checker and make sure there are no (relevant) violations.
#### Export Gerber files

View File

@ -8,7 +8,7 @@ meta:
# Required version of Ergogen
engine: "4.1.0"
# Version of the board
version: "1.1"
version: "1.0"
author: binaryDiv
# Define units and default values used throughout this file
@ -29,409 +29,268 @@ units:
$default_spread: cx
$default_padding: cy
# Vertical offset between function key row and main zone
function_offset: 3
# Vertical offset between function keys and primary keys
function_zone_offset: 3
# Horizontal offset/gap between function key groups (Esc, F1-F4, F5-F8, F9-F12)
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)
# Define the points (i.e. keys)
points:
key:
# Tag all points as "is_key" unless overwritten
tags: [ is_key ]
# Use 5V as VCC for all RGB LEDs, except for the first one in the chain
led_vcc_net: VCC
zones:
# Main keyboard zone
main:
# Primary keyboard zone
primary:
# Set anchor to fix placement on KiCad sheet
anchor:
shift: [ 75, -175 ]
shift: [ 100, -150 ]
# Key rows (from bottom to top)
rows:
# Modifier row (Ctrl etc., but excluding the thumb keys)
# Modifier row (Ctrl, ..., but excluding the thumb keys)
mods:
row_net: ROW1
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
row_net: GP17
led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
tags: [ is_key, flip_led ]
# Bottom letter row
# Bottom letter row (Shift, ZXCV...)
bottom:
row_net: ROW2
led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
tags: [ is_key, flip_led ]
row_net: GP16
led_previous_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
# Home letter row
# Middle/home letter row (Caps Lock, ASDFG...)
home:
row_net: ROW3
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
# Top letter row
top:
row_net: ROW4
led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
row_net: GP15
led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
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:
row_net: ROW5
led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
row_net: GP13
led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"
tags: [ is_key, flip_led ]
# Key columns (from left to right)
columns:
# Left-most column (extra keys)
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.)
# Left-most column (`, Tab, Caps Lock, Shift, Ctrl)
zero:
key.stagger: 1/8cy
key.column_net: COL2
key.neighbor_col_left: left
key.column_net: GP8
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)
one:
key.stagger: 1/8cy
key.column_net: COL3
key.column_net: GP7
key.neighbor_col_left: zero
key.neighbor_col_right: two
# Second letter column (2, W)
two:
key.stagger: 1/8cy
key.column_net: COL4
key.column_net: GP6
key.neighbor_col_left: one
key.neighbor_col_right: three
rows.mods.skip: true
# Third letter column (3, E)
three:
key.column_net: COL5
key.column_net: GP5
key.neighbor_col_left: two
key.neighbor_col_right: four
rows.mods.skip: true
# Fourth letter column (4, R)
four:
key.stagger: -(1/4cy)
key.column_net: COL6
key.column_net: GP4
key.neighbor_col_left: three
key.neighbor_col_right: five
rows.mods.skip: true
# Fifth letter column (5, T)
five:
key.stagger: -(1/4cy)
key.column_net: COL7
key.column_net: GP3
key.neighbor_col_left: four
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
# (Gap with arrow keys in the middle: Matrix column 8)
# Sixth letter column (6, Y)
six:
key.spread: cx + split_gap_width
key.column_net: COL9
key.column_net: GP2
key.neighbor_col_left: five
key.neighbor_col_right: seven
rows.home.led_next_key: arrow_keys_right
rows.mods.skip: true
# Seventh letter column (7, U)
seven:
key.stagger: 1/4cy
key.column_net: COL10
key.column_net: GP1
key.neighbor_col_left: six
key.neighbor_col_right: eight
rows.mods.skip: true
# Eighth letter column (8, I)
eight:
key.stagger: 1/4cy
key.column_net: COL11
key.column_net: GP0
key.neighbor_col_left: seven
key.neighbor_col_right: nine
rows.mods.skip: true
# Nineth letter column (9, O)
nine:
key.column_net: COL12
key.column_net: GP25
key.neighbor_col_left: eight
key.neighbor_col_right: ten
rows.mods.skip: true
# Tenth letter column (0, P)
ten:
key.stagger: -(1/8cy)
key.column_net: COL13
key.column_net: GP24
key.neighbor_col_left: nine
key.neighbor_col_right: eleven
rows.mods.led_next_key: mirror_thumb_one
rows.mods.skip: true
# Eleventh letter column (-, [)
eleven:
key.stagger: -(1/8cy)
key.column_net: COL14
key.column_net: GP23
key.neighbor_col_left: ten
key.neighbor_col_right: twelve
rows.mods.led_previous_key: thumb_mirror_one_default
# Twelfth letter column (=, ])
twelve:
key.stagger: -(1/8cy)
key.column_net: COL15
key.column_net: GP22
key.neighbor_col_left: eleven
rows.top.led_next_key: main_twelve_home
rows.bottom.led_next_key: main_twelve_mods
rows.top.led_previous_key: primary_twelve_numbers
rows.bottom.led_previous_key: primary_twelve_home
# Thumb fan
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:
ref: main_one_mods
shift: [ cx + 1, 0 ]
ref: primary_two_mods
shift: [ 2, 0 ]
# Zone-wide key settings
key:
row_net: ROW1
# Rotate the thumb keys around the bottom-left corner of the key
key:
row_net: GP17
tags: [ is_key, flip_led ]
origin: [ -0.5cx, -0.5cy ]
splay: -6
splay: -4
# Define thumb keys
columns:
one:
key.column_net: COL4
key.mirror.column_net: COL12
key.led_next_key: main_one_mods
key.mirror.led_next_key: mirror_thumb_two
key.column_net: GP6
key.mirror.column_net: GP24
key.led_previous_key: primary_one_mods
key.mirror.led_previous_key: thumb_mirror_two_default
two:
key.column_net: COL5
key.mirror.column_net: COL11
key.led_next_key: thumb_one
key.mirror.led_next_key: mirror_thumb_three
key.column_net: GP5
key.mirror.column_net: GP25
key.led_previous_key: thumb_one_default
key.mirror.led_previous_key: thumb_mirror_three_default
three:
key.column_net: COL6
key.mirror.column_net: COL10
key.led_next_key: thumb_two
key.mirror.led_next_key: mirror_thumb_four
key.column_net: GP4
key.mirror.column_net: GP0
key.led_previous_key: thumb_two_default
key.mirror.led_previous_key: thumb_mirror_four_default
four:
key.column_net: COL7
key.mirror.column_net: COL9
key.led_next_key: thumb_three
key.mirror.led_next_key: thumb_center
key.column_net: GP3
key.mirror.column_net: GP1
key.led_previous_key: thumb_three_default
key.mirror.led_previous_key: thumb_four_default
# Mirror the thumb fan
mirror:
ref: main_five_bottom
shift: [ 0.5cx, 0.5cy ]
distance: split_gap_width
ref: primary_five_numbers
shift: [ 0.5cx, 0.5cx ]
distance: 1cx
# Single key centered between thumb keys
thumb_center:
anchor:
# 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 key row above the primary zone (Esc + 8 more keys)
# (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
# used as F-keys when needed, but will more likely be used for macros and other special functions.)
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:
# To center the F keys on the top of the keyboard:
# - Get center point between keys 3 and 8 (they have the highest staggering)
# - 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 ]
ref: primary_zero_numbers
shift: [ 0, cy + function_zone_offset ]
# Single row zone: Assign row net
key:
row_net: ROW6
tags: [ is_key, flip_led ]
row_net: GP12
# Define function keys
columns:
# Escape key
esc:
key.shift: [ 0, -cy/8 ]
key.column_net: COL1
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
key.column_net: GP8
key.led_previous_key: function_f1_default
# Block of 4 keys with gap
# Block of 4 keys with 6mm offset
f1:
key.spread: cx + function_gap
key.column_net: COL2
key.led_next_key: function_f2
key.spread: cx + 6
key.column_net: GP7
key.led_previous_key: function_f2_default
f2:
key.column_net: COL3
key.led_next_key: function_f3
key.column_net: GP6
key.led_previous_key: function_f3_default
f3:
key.column_net: COL4
key.led_next_key: function_f4
key.column_net: GP5
key.led_previous_key: function_f4_default
f4:
key.column_net: COL5
key.led_next_key: function_f5
key.column_net: GP4
key.led_previous_key: function_f5_default
# Block of 4 keys with gap
# Block of 4 keys with 6mm offset
f5:
key.spread: cx + function_gap
key.column_net: COL6
key.led_next_key: function_f6
key.spread: cx + 6
key.column_net: GP3
key.led_previous_key: function_f6_default
f6:
key.column_net: COL7
key.led_next_key: function_f7
# Skip matrix column 8 (arrow keys)
key.column_net: GP2
key.led_previous_key: function_f7_default
f7:
key.column_net: COL9
key.led_next_key: function_f8
key.column_net: GP1
key.led_previous_key: function_f8_default
f8:
key.column_net: COL10
key.led_next_key: function_f9
key.column_net: GP0
key.led_previous_key: status_led
# Block of 4 keys with gap
f9:
key.spread: cx + function_gap
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:
# Additional points for special components in the upper right corner: Status LED, reset button, rotary encoder
special:
# Align points with the function key zone
anchor:
# Place in the center of the keyboard, above the arrow keys
aggregate.parts:
- main_five_numbers
- main_six_numbers
shift: [ 0, 1.5 ]
ref: primary_ten_numbers
shift: [ 0, cy + function_zone_offset ]
# This is an auxiliary point to place the controller, not an actual key
# These are not actually keys
key:
tags: [ is_controller ]
width: 18
height: 36
tags: [ no_key ]
# Rotary encoder in the center of the keyboard (on top of the controller board)
rotary_center:
anchor:
ref: controller
rotate: -90
# Define points
columns:
# Neopixel status LED (first in the chain, see comment in PCB footprints for details)
status_led:
key:
width: 3.4
height: 3.0
# Reset button
reset_button:
key:
shift: [ -0.25cx - 1, 0 ]
width: 6.5
height: 6.5
# Rotary encoder in the upper right corner
rotary:
key:
row_net: GP12
column_net: GP22
shift: [ -0.25cx, 0 ]
width: 20.5
height: 20.5
tags: [ is_rotary_encoder ]
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
rotary_top_right:
anchor:
ref: function_f12
shift: [ cx + 2 + function_gap, -cy/8 ]
key:
tags: [ is_rotary_encoder ]
width: 2 * rotary_encoder_radius
height: 2 * rotary_encoder_radius
# Matrix position of the switch: Function row, right of F12
row_net: ROW6
column_net: COL15
# Generate outlines that can be used in the PCB and for 3D models
outlines:
# Outline of the key caps
keys:
# 1u keys
- what: rectangle
operation: stack
where: [ [ is_key, -1_5u ] ]
where: is_key
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:
@ -440,77 +299,46 @@ outlines:
size: [ 14, 14 ]
- what: circle
operation: stack
radius: rotary_encoder_radius
radius: 10
# Outline for the PCB
board_outline:
board:
- what: polygon
points:
# Top left corner
- ref: &corner_top_left_anchor
- ref: main_left_numbers
affect: x
- ref: function_f1
affect: y
- shift: [ -0.5kcx, 0.5kcy ]
# Extend all corners 4mm to the sides to get a 4mm border
shift: [ -4, 4 ]
# Top right corner
- ref: &corner_top_right_anchor
- ref: main_twelve_numbers
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: primary_zero_numbers
shift: [ -0.5cx + 1, 1.5cy + function_zone_offset + 2 ]
- ref: primary_twelve_numbers
shift: [ 0.5cx - 1, 1.5cy + function_zone_offset + 2]
- ref: primary_twelve_mods
shift: [ 0.5cx - 1, -0.5cy + 2 ]
- 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:
shift: [ 0.5cx, -0.5cy + 2 ]
- 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
shift: [ 0.5cx, -0.5cy + 2 ]
- ref: primary_zero_mods
shift: [ -0.5cx + 1, -0.5cy + 2 ]
expand: 4
# Preview version of board with key caps and components for visualization
board_preview:
- board_outline
- board
- ^keys
# RP2040 Pico Mini controller board
# RP2040 controller board
- what: rectangle
operation: stack
where: is_controller
size: [ 18, 36 ]
where: primary_eleven_numbers
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
- what: outline
name: rotary_encoder
@ -520,12 +348,12 @@ outlines:
# Cutouts for the switches
switch_cutouts:
- what: rectangle
where: is_key
where: true
size: 14
# Board outline with switch cutouts
switch_plate:
- board_outline
- board
- -switch_cutouts
# Generate the PCB
@ -534,48 +362,20 @@ pcbs:
# Define outline (edges) of the board based on the outlines defined above
outlines:
main:
outline: board_outline
outline: board
# Define PCB components
footprints:
controller:
what: rp2040_pico_mini
where: is_controller
what: rp2040_purple
where:
ref: primary_eleven_numbers
shift: [ 0, cy + function_zone_offset ]
rotate: 90
params:
# Mount the controller board on the backside of the PCB
orientation: down
# Use 5V from USB as VCC net
5V: 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
# Use VBUS (5V from USB) as VCC net
VBUS: VCC
choc_hotswap:
what: choc_pretty
@ -585,10 +385,10 @@ pcbs:
params:
keycaps: true
hotswap: true
from: "diode_{{name}}"
from: "{{colrow}}"
to: "{{column_net}}"
key_diode:
diode:
what: diode_smd
where: is_key
adjust:
@ -597,21 +397,21 @@ pcbs:
resist: true
params:
side: B
from: "diode_{{name}}"
from: "{{colrow}}"
to: "{{row_net}}"
rotary_encoder:
what: rotary_modified
where: is_rotary_encoder
params:
from: "diode_{{name}}"
from: "{{colrow}}"
to: "{{column_net}}"
A: "{{name}}_a"
B: "{{name}}_b"
A: GP10
B: GP11
C: GND
rotary_encoder_diode:
$extends: pcbs.eepyboard.footprints.key_diode
diode_rotary_encoder:
$extends: pcbs.eepyboard.footprints.diode
where: is_rotary_encoder
adjust:
shift: [ 0, 0 ]
@ -626,9 +426,8 @@ pcbs:
shift: [ 0, 4.7 ]
params:
side: B
din: "led_din_{{name}}"
dout: "led_din_{{led_next_key}}"
VCC: "{{led_vcc_net}}"
din: "led_dout_{{led_previous_key}}"
dout: "led_dout_{{zone.name}}_{{colrow}}"
led_capacitor:
what: cap_0805
@ -639,12 +438,12 @@ pcbs:
resist: true
params:
side: B
from: "{{led_vcc_net}}"
from: VCC
to: GND
led_chip_flipped:
$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 ] ]
adjust.rotate: 180
@ -656,82 +455,93 @@ pcbs:
rotate: 270
resist: true
# The first SK6812 chip in the chain has to run on a reduced voltage to serve as a makeshift level converter for
# all following RGB LEDs.
# 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)
# for the control signal. By reducing the power voltage with a diode, the threshold is reduced as well, so that
# the signal of the RP2040 is strong enough.
first_led_voltage_diode:
what: diode_smd
where: function_esc
# Additional first LED (not bound to a key) that serves as a status LED.
# Additionally, it runs on a reduced voltage to serve as a level converter for the next LED: The RP2040 runs on
# 3.3V, but the SK6812 runs on 5V and requires at least 3.5V (5V * 0.7) for the control signal. By reducing the
# power voltage with a diode, the threshold is reduced as well, so that the signal of the RP2040 is strong enough.
status_led_chip:
what: sk6812_mini_e
where: special_status_led
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:
shift: [ 9.5, 2.25 ]
rotate: -90
shift: [ -6, -0.2 ]
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:
side: B
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:
what: mountinghole_m2
where:
ref: *corner_top_left_anchor
shift: [ 2, -2 ]
ref: function_esc
shift: [ -9, 11.5 ]
mounting_hole_top_center:
what: mountinghole_m2
where:
ref: function_f4
shift: [ 12.5, 0 ]
mounting_hole_top_right:
what: mountinghole_m2
where:
ref: *corner_top_right_anchor
shift: [ -2, -2 ]
ref: primary_twelve_numbers
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:
what: mountinghole_m2
where:
ref: main_zero_mods
shift: [ 0, -13.5 ]
ref: primary_one_mods
shift: [ 10.5, -12 ]
mounting_hole_bottom_center:
what: mountinghole_m2
where:
ref: primary_six_bottom
shift: [ 0, -23 ]
mounting_hole_bottom_right:
what: mountinghole_m2
where:
ref: main_eleven_mods
shift: [ 0, -13.5 ]
ref: primary_eleven_mods
shift: [ -10.5, -12 ]
# Render text with project name and copyright onto the PCB
copyright_text:
what: text
where:
ref: thumb_one
shift: [ 0, -16.5 ]
rotate: -0.3 # total rotation: -6.3 (including key rotation)
ref: primary_six_bottom
shift: [ 0, -15 ]
params:
text: |-
eepyBoard v1.1 by binaryDiv
eepyBoard v1.0 by binaryDiv
(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)}
)
`;
}
}