# Ergogen config for the eepyBoard
#
# Reference information:
# - Keycap size (FK MBK keycaps): 17.5mm x 16.5mm

# Metadata
meta:
  # Required version of Ergogen
  engine: "4.1.0"
  # Version of the board
  version: "1.2"
  author: binaryDiv

# Define units and default values used throughout this file
units:
  # Redefine choc units (keycap size + 1.5mm clearance)
  cx: 19
  cy: 18

  # Size of keycaps
  kcx: 17.5
  kcy: 16.5

  # Size of key rectangle in demo view (keycap size)
  $default_width: 17.5
  $default_height: 16.5

  # Actual spacing of keys
  $default_spread: cx
  $default_padding: cy

  # Vertical offset between function key row and main zone
  function_row_offset: 3

  # Outline border (border between keys and PCB edge)
  border_left: -6
  border_right: 6
  border_top: 5
  border_bottom: -4

  # 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:
  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:
      # Set anchor to fix placement on KiCad sheet
      anchor:
        shift: [ 75, -175 ]

      # Key rows (from bottom to top)
      rows:
        # Modifier row (Ctrl etc., but excluding the thumb keys)
        mods:
          row_net: ROW1
          led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
          tags: [ is_key, flip_led ]

        # Bottom letter row
        bottom:
          row_net: ROW2
          led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"

        # Home letter row
        home:
          row_net: ROW3
          led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
          tags: [ is_key, flip_led ]

        # Top letter row
        top:
          row_net: ROW4
          led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}"

        # Number row
        numbers:
          row_net: ROW5
          led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}"
          tags: [ is_key, flip_led ]

      # Key columns (from left to right)
      columns:
        # Left-most column (`, Tab, Shift, etc.)
        left:
          key.column_net: COL1
          key.neighbor_col_right: one
          rows.top.led_next_key: main_left_home
          rows.bottom.led_next_key: main_left_mods
        # First letter column (1, Q)
        one:
          key.column_net: COL2
          key.neighbor_col_left: left
          key.neighbor_col_right: two
          rows.mods.led_next_key: thumb_one
        # Second letter column (2, W)
        two:
          key.column_net: COL3
          key.neighbor_col_left: one
          key.neighbor_col_right: three
          rows.mods.skip: true
        # Third letter column (3, E)
        three:
          key.column_net: COL4
          key.neighbor_col_left: two
          key.neighbor_col_right: four
          rows.mods.skip: true
        # Fourth letter column (4, R)
        four:
          key.column_net: COL5
          key.neighbor_col_left: three
          key.neighbor_col_right: five
          rows.mods.skip: true
        # Fifth letter column (5, T)
        five:
          key.column_net: COL6
          key.neighbor_col_left: four
          key.neighbor_col_right: six
          rows.mods.skip: true
        # Sixth letter column (6, Y)
        six:
          key.column_net: COL7
          key.neighbor_col_left: five
          key.neighbor_col_right: seven
          rows.mods.skip: true
        # Seventh letter column (7, U)
        seven:
          key.column_net: COL8
          key.neighbor_col_left: six
          key.neighbor_col_right: eight
          rows.mods.skip: true
        # Eighth letter column (8, I)
        eight:
          key.column_net: COL9
          key.neighbor_col_left: seven
          key.neighbor_col_right: nine
          rows.mods.skip: true
        # Nineth letter column (9, O)
        nine:
          key.column_net: COL10
          key.neighbor_col_left: eight
          key.neighbor_col_right: ten
          rows.mods.skip: true
        # Tenth letter column (0, P)
        ten:
          key.column_net: COL11
          key.neighbor_col_left: nine
          key.neighbor_col_right: eleven
        # Eleventh letter column (-, [)
        eleven:
          key.column_net: COL12
          key.neighbor_col_left: ten
          key.neighbor_col_right: twelve
        # Twelfth letter column (=, ])
        twelve:
          key.column_net: COL13
          key.neighbor_col_left: eleven
          key.neighbor_col_right: right
          rows.numbers.led_next_key: main_right_top
        # Right-most column (Enter, Shift etc.)
        right:
          key.column_net: COL14
          key.neighbor_col_left: twelve
          # Skip right-most key in numbers row because that wouldn't fit with the controller board
          rows.numbers.skip: true
          rows.home.led_next_key: main_right_bottom
          # Use placeholder to mark end of the RGB LED chain (unused net name)
          rows.mods.led_next_key: END_OF_CHAIN

    # Thumb fan
    thumb:
      # Position thumb keys based on the third key in the modifier row
      anchor:
        ref: main_one_mods
        shift: [ cx + 1, 0 ]

      # Zone-wide key settings
      key:
        row_net: ROW1
        tags: [ is_key, flip_led ]
        # Rotate the thumb keys around the bottom-left corner of the key
        origin: [ -0.5cx, -0.5cy ]
        splay: -4

      # Define thumb keys
      columns:
        one:
          key.column_net: COL3
          key.mirror.column_net: COL10
          key.led_next_key: thumb_two
          key.mirror.led_next_key: main_ten_mods
        two:
          key.column_net: COL4
          key.mirror.column_net: COL9
          key.led_next_key: thumb_three
          key.mirror.led_next_key: mirror_thumb_one
        three:
          key.column_net: COL5
          key.mirror.column_net: COL8
          key.led_next_key: thumb_center
          key.mirror.led_next_key: mirror_thumb_two

      # Mirror the thumb fan
      mirror:
        ref: main_five_bottom
        shift: [ 0.5cx, 0.5cy ]

    # Single key centered between thumb keys (space key)
    thumb_center:
      anchor:
        # Center between the two inner thumb keys
        aggregate.parts:
          - thumb_three
          - mirror_thumb_three
        shift: [ 0, -0.125cy ]

      # Key settings (instead of rows/columns)
      key:
        width: 1.5kcx
        row_net: ROW1
        column_net: COL6
        led_next_key: mirror_thumb_three
        tags: [ is_key, flip_led, 1_5u ]

    # Function key row above the main zone (Esc, F1-F12)
    function:
      # Position function key row above main zone with a vertical offset
      anchor:
        ref: main_left_numbers
        shift: [ 0, cy + function_row_offset ]

      # Single row zone: Assign row net
      key:
        row_net: ROW6

      # Define function keys
      columns:
        # Escape key
        esc:
          key.column_net: COL1
          key.led_next_key: main_left_numbers

        # F1 to F12 keys
        f1:
          key.column_net: COL2
          key.led_next_key: function_esc
        f2:
          key.column_net: COL3
          key.led_next_key: function_f1
        f3:
          key.column_net: COL4
          key.led_next_key: function_f2
        f4:
          key.column_net: COL5
          key.led_next_key: function_f3
        f5:
          key.column_net: COL6
          key.led_next_key: function_f4
        f6:
          key.column_net: COL7
          key.led_next_key: function_f5
        f7:
          key.column_net: COL8
          key.led_next_key: function_f6
        f8:
          key.column_net: COL9
          key.led_next_key: function_f7
        f9:
          key.column_net: COL10
          key.led_next_key: function_f8
        f10:
          key.column_net: COL11
          key.led_next_key: function_f9
        f11:
          key.column_net: COL12
          key.led_next_key: function_f10
        f12:
          key.column_net: COL13
          key.led_next_key: function_f11
          # F12 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

    # Rotary encoder in the top right corner of the keyboard
    rotary_top_right:
      anchor:
        ref: function_f12
        shift: [ cx + 3, 0 ]
        rotate: 90
      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: COL14

    # RP2040 Pico Mini controller board
    controller:
      anchor:
        # Place in top-right corner (right to F12, with rotary encoder on top)
        - ref: rotary_top_right
          affect: xy
        - shift: [ 0, -0.5cy + 2.5 ]

      # This is an auxiliary point to place the controller, not an actual key
      key:
        tags: [ is_controller ]
        width: 18
        height: 36

# 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 ] ]
      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:
    - what: rectangle
      operation: stack
      size: [ 14, 14 ]
    - what: circle
      operation: stack
      radius: rotary_encoder_radius

  # Outline for the PCB
  board_outline:
    - what: polygon
      points:
        # Top left corner
        - ref:
            - ref: function_esc
              shift: [ -0.5kcx, 0.5kcy ]
          # Extend all corners to the sides to get a border
          shift: [ border_left, border_top ]

        # Top right corner
        - ref:
            - ref: main_right_top
              affect: x
            - ref: function_f12
              affect: y
            - shift: [ 0.5kcx, 0.5kcy ]
          shift: [ border_right, border_top ]

        # Bottom right corner
        - ref:
            - ref: main_right_mods
              shift: [ 0.5kcx, -0.5kcy ]
          shift: [ border_right, border_bottom ]

        # Bottom center "corner" below space key (needs to be post-processed in KiCad to get a smooth arc, see below!)
        - ref:
            - ref: thumb_center
              shift: [ 0, -0.5kcy ]
          shift: [ 0, border_bottom ]

        # Bottom left corner
        - ref:
            - ref: main_left_mods
              shift: [ -0.5kcx, -0.5kcy ]
          shift: [ border_left, border_bottom ]

      # Round off the corners with a fillet.
      # NOTE: To get a smoother arc on the bottom of the board, do some post-processing in KiCad:
      # 1. Remove the tiny fillet arc between the two diagonal bottom lines.
      # 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 300mm radius.
      fillet: 5

  # Preview version of board with key caps and components for visualization
  board_preview:
    - board_outline
    - ^keys

    # RP2040 Pico Mini controller board
    - what: rectangle
      operation: stack
      where: is_controller
      size: [ 18, 36 ]

    # Rotary encoder
    - what: outline
      name: rotary_encoder
      operation: stack
      where: is_rotary_encoder

  # Cutouts for the switches
  switch_cutouts:
    - what: rectangle
      where: is_key
      size: 14

  # Board outline with switch cutouts
  switch_plate:
    - board_outline
    - -switch_cutouts

# Generate the PCB
pcbs:
  eepyboard:
    # Define outline (edges) of the board based on the outlines defined above
    outlines:
      main:
        outline: board_outline

    # Define PCB components
    footprints:
      controller:
        what: rp2040_pico_mini
        where: is_controller
        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 F12 key
          GP29: led_din_function_f12
          # Matrix columns
          GP27: COL1
          GP26: COL2
          GP25: COL3
          GP23: COL4
          GP22: COL5
          GP21: COL6
          GP18: COL7
          GP17: COL8
          GP13: COL9
          GP12: COL10
          GP11: COL11
          GP10: COL12
          GP9: COL13
          GP8: COL14
          # Matrix rows
          GP0: ROW1
          GP1: ROW2
          GP2: ROW3
          GP3: ROW4
          GP19: ROW5
          GP28: ROW6
          # Rotary encoder
          GP6: rotary_top_right_a
          GP7: rotary_top_right_b

      choc_hotswap:
        what: choc_pretty
        where: is_key
        # Rotate footprint so that the hotswap socket is at the bottom and the LED can be at the top
        adjust.rotate: 180
        params:
          keycaps: true
          hotswap: true
          from: "diode_{{name}}"
          to: "{{column_net}}"

      key_diode:
        what: diode_smd
        where: is_key
        adjust:
          shift: [ 7, -7 ]
          rotate: 90
          resist: true
        params:
          side: B
          from: "diode_{{name}}"
          to: "{{row_net}}"

      rotary_encoder:
        what: rotary_encoder_ec12
        where: is_rotary_encoder
        params:
          from: "diode_{{name}}"
          to: "{{column_net}}"
          A: "{{name}}_a"
          B: "{{name}}_b"
          C: GND

      rotary_encoder_diode:
        $extends: pcbs.eepyboard.footprints.key_diode
        where: is_rotary_encoder
        adjust:
          shift: [ 0, 0 ]
          rotate: 0

      led_chip:
        what: sk6812_mini_e
        # Place unrotated LEDs in all rows without the flip_led tag
        where: [ [ is_key, -flip_led ] ]
        # Position LEDs on the LED hole in the Choc switches
        adjust:
          shift: [ 0, 4.7 ]
        params:
          side: B
          din: "led_din_{{name}}"
          dout: "led_din_{{led_next_key}}"
          VCC: "{{led_vcc_net}}"

      led_capacitor:
        what: capacitor_smd_1206
        where: [ [ is_key, -flip_led ] ]
        adjust:
          shift: [ -6.5, 4.75 ]
          rotate: 90
          resist: true
        params:
          side: B
          from: "{{led_vcc_net}}"
          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
        where: [ [ is_key, flip_led ] ]
        adjust.rotate: 180

      led_capacitor_flipped:
        $extends: pcbs.eepyboard.footprints.led_capacitor
        where: [ [ is_key, flip_led ] ]
        adjust:
          shift: [ 6.5, 4.75 ]
          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_f12
        adjust:
          shift: [ 0, 8.5 ]
          rotate: 0
        params:
          side: B
          from: VCC
          to: VCC_first_led

      # Mounting holes: Top row (above and between Esc/F1, F4/F5, F8/F9, F12/rotary)
      mounting_hole_top1:
        what: mountinghole_m2
        where:
          ref: function_f1
          shift: [ -0.5cx, 0.5cy + 1.5 ]

      mounting_hole_top2:
        what: mountinghole_m2
        where:
          ref: function_f5
          shift: [ -0.5cx, 0.5cy + 1.5 ]

      mounting_hole_top3:
        what: mountinghole_m2
        where:
          ref: function_f8
          shift: [ 0.5cx, 0.5cy + 1.5 ]

      mounting_hole_top4:
        what: mountinghole_m2
        where:
          ref: function_f12
          shift: [ 0.5cx - 1, 0.5cy + 1.5 ]

      # Mounting holes: Top left corner (left of Esc)
      mounting_hole_top_left:
        what: mountinghole_m2
        where:
          ref: function_esc
          shift: [ -12, 0 ]

      # Mounting holes: "Top" right corner (right edge, below the controller board)
      mounting_hole_top_right:
        what: mountinghole_m2
        where:
          ref: main_right_top
          shift: [ 0.5cx - 0.5, 0.5cy + 1.25 ]

      # Mounting holes: Bottom center, below C and N keys (with copyright text in between)
      mounting_hole_bottom_center_left:
        what: mountinghole_m2
        where:
          ref: main_four_bottom
          shift: [ 4, -12.25 ]

      mounting_hole_bottom_center_right:
        what: mountinghole_m2
        where:
          ref: main_seven_bottom
          shift: [ -4, -12.25 ]

      # Mounting holes: Bottom left corner (left of Ctrl; below left thumb keys)
      mounting_hole_bottom_left1:
        what: mountinghole_m2
        where:
          ref: main_one_mods
          shift: [ 0.5cx, -11.75 ]

      mounting_hole_bottom_left2:
        what: mountinghole_m2
        where:
          ref: main_left_mods
          shift: [ -12, 0 ]

      # Mounting holes: Bottom right corner (right of bottom-right key; below right thumb keys)
      mounting_hole_bottom_right1:
        what: mountinghole_m2
        where:
          ref: main_twelve_mods
          shift: [ -0.5cx, -11.75 ]

      mounting_hole_bottom_right2:
        what: mountinghole_m2
        where:
          ref: main_right_mods
          shift: [ 12, 0 ]

      # Render text with project name and copyright onto the PCB
      copyright_text:
        what: text
        where:
          ref: thumb_center
          shift: [ 0, 13.25 ]
        params:
          text: |-
            eepyBoard v1.2 by binaryDiv
            (c) 2024 (MIT License)
