Notes about the position file

Position files are quite simple. You can generate them as plain text (ASCII) or as a spreadsheet (CSV).

But some conventions can make them tricky. Some manufacturers, like JLCPCB, uses conventions that are incompatible with KiCad.

Simple pick and place rotation correction

The following blog explains how to adapt the position files generated by KiCad to what JLCPCB needs. To achieve it the author uses a script called JLCKicadTools.

You can achieve the same using KiBot. Here is a configuration example that generates the BoM and position files in the same way JLCKicadTools does:

kibot:
  version: 1

import:
  - file: JLCPCB

The JLCPCB templates creates files compatible with JLCPB, including adjusts for the most common components rotations.

This assumes KiBot can figure out which field was used to indicate the part number used by LCSC (JLC uses LCSC as supplier). If you need to force the field name you can use something like:

kibot:
  version: 1

globals:
  # Make it match the name used by your project
  field_lcsc_part: LCSC

import:
  - file: JLCPCB

This example is very simple, but also very specific for a particular manufacturer. For this reason we will see how the JLCPCB template is implemented, so you can adapt it. A similar result can be obtained using:

kibot:
  version: 1

filters:
  - name: only_jlc_parts
    comment: 'Only parts with JLC code'
    type: generic
    include_only:
      - column: _field_lcsc_part
        regex: '^C\d+'

outputs:
  - name: 'position'
    comment: "Pick and place file, JLC style"
    type: position
    options:
      pre_transform: _rot_footprint_jlcpcb
      output: '%f_cpl_jlc.%x'
      format: CSV
      units: millimeters
      separate_files_for_front_and_back: false
      only_smd: true
      columns:
        - id: Ref
          name: Designator
        - Val
        - Package
        - id: PosX
          name: "Mid X"
        - id: PosY
          name: "Mid Y"
        - id: Rot
          name: Rotation
        - id: Side
          name: Layer

  - name: 'bom'
    comment: "BoM for JLC"
    type: bom
    options:
      output: '%f_%i_jlc.%x'
      exclude_filter: 'only_jlc_parts'
      ref_separator: ','
      columns:
        - field: Value
          name: Comment
        - field: References
          name: Designator
        - Footprint
        - field: _field_lcsc_part
          name: 'LCSC Part #'
      csv:
        hide_pcb_info: true
        hide_stats_info: true
        quote_all: true

The only_jlc_parts filter is used to generate the BoM. The special field name _field_lcsc_part``is the result of KiBot autodetection. You can force a value using the global option ``field_lcsc_part, like in:

globals:
  # Make it match the name used by your project
  field_lcsc_part: LCSC

Note that the author of the blog simply used Field4 for this and his script searches for any field containing the ^C\d+ pattern. KiBot can also autodetect the field name, but I think this isn’t a good idea and I suggest using a defined name, using the above definition.

The _rot_footprint_jlcpcb is an internal filter of type rot_footprint. Here is the same configuration file making explicit use of the rotation filter:

kibot:
  version: 1

globals:
  field_lcsc_part: LCSC

filters:
  - name: only_jlc_parts
    comment: 'Only parts with JLC code'
    type: generic
    include_only:
      - column: _field_lcsc_part
        regex: '^C\d+'

  - name: fix_rotation
    comment: 'Adjust rotation for JLC'
    type: rot_footprint
    negative_bottom: false
    mirror_bottom: true

outputs:
  - name: 'position'
    comment: "Pick and place file, JLC style"
    type: position
    options:
      pre_transform: fix_rotation
      output: '%f_cpl_jlc.%x'
      format: CSV
      units: millimeters
      separate_files_for_front_and_back: false
      only_smd: true
      columns:
        - id: Ref
          name: Designator
        - Val
        - Package
        - id: PosX
          name: "Mid X"
        - id: PosY
          name: "Mid Y"
        - id: Rot
          name: Rotation
        - id: Side
          name: Layer

  - name: 'bom'
    comment: "BoM for JLC"
    type: bom
    options:
      output: '%f_%i_jlc.%x'
      exclude_filter: 'only_jlc_parts'
      ref_separator: ','
      columns:
        - field: Value
          name: Comment
        - field: References
          name: Designator
        - Footprint
        - field: _field_lcsc_part
          name: 'LCSC Part #'
      csv:
        hide_pcb_info: true
        hide_stats_info: true
        quote_all: true

As you can see we now create a filter named fix_rotation of type rot_footprint:

- name: fix_rotation
  comment: 'Adjust rotation for JLC'
  type: rot_footprint
  negative_bottom: false
  mirror_bottom: true

Using it, instead of the internal filter named _rot_footprint_jlcpcb, is the same here. But you can then customize the filter.

In order to add a new rotation or just change an existing one you just need to use the rotations option. As an example: the internal list of rotations rotates QFN packages by 270 degrees, now suppose you want to rotate them just 90 degrees. The filter will look like this:

- name: fix_rotation
  comment: 'Adjust rotation for JLC'
  type: rot_footprint
  negative_bottom: false
  mirror_bottom: true
  rotations:
    - ["^QFN-", 90.0]

This regular expression will match any footprint starting with QFN- and rotate it 90 degrees.

Note that the order in this list is relevant. The first match will be applied.

You can also use a customized filter using the internal template, here is an example:

kibot:
  version: 1

filters:
  - name: fix_rotation
    comment: 'Adjust rotation for JLC'
    type: rot_footprint
    negative_bottom: false
    mirror_bottom: true
    rotations:
      - ["^QFN-", 90.0]

import:
  - file: JLCPCB
    definitions:
      _KIBOT_POS_PRE_TRANSFORM: fix_rotation

You can create filters for different assembly houses and generate independent position files for each manufacturer. The next sections explains more details related to this.

Fixing offsets in position files

The same mechanism used to change rotations can be applied to offsets in the positions. You just need to add new offsets to the offsets option, like in the following example:

- name: fix_rotation
  comment: 'Adjust rotation for JLC'
  type: rot_footprint
  negative_bottom: false
  mirror_bottom: true
  offsets:
    - ["^QFN-20", "1,0.5"]

This will shift the component center of QFN-20 footprints 1 mm in the X axis and 0.5 mm in the Y axis. The signs of this correction depends on many options, in general they are compatible with the JLCKicadTools tool.

Understanding the rotations problem

The rotation you get in the position file depends on the following factors:

  1. How KiCad footprints are oriented. In general KiCad footprints has their pin 1 at the top left corner.

  2. How the manufacturer machine software interprets the angle. KiCad mirrors components on the bottom side, but JLCPCB doesn’t (november 2023)

  3. How chips are oriented in the tape reel. Some manufacturers even support multiple orientations for the same component.

This means the problem isn’t that simple to solve. The internal database of rotations is just a list of common cases, but can’t solve any problem. In fact you can have two components with the same footprint and different rotations in the same project.

To solve this problem you can:

  • Provide a list of rotations and offsets using the rotations_and_offsets option

  • Use special fields to specify the rotations and offsets

Fine grained rotation and/or offset adjusts

Using the configuration file

The rotations and offsets options of the rot_footprint filter can apply adjusts to components based on their footprint. This covers the most common cases. But if you have two components with the same footprint and different rotation adjusts you can’t solve the problem. Of course you could use different names for the footprints, even when they are the same, just to allow matching them using different regular expressions. But you can also use the rotations_and_offsets option. Another case is when you need to provide adjusts for different manufacturers and the rotations for the same footprint depends on the manufacturer.

When using the rotations_and_offsets option you can specify which field is used to match the component. So you can use any field you want, not just the footprint. This gives you freedom to match components using customized fields. The following example will adjust U103 rotation by 180 degrees:

filters:
  - name: fix_rotation
    comment: 'Adjust rotation for JLC'
    type: rot_footprint
    negative_bottom: false
    mirror_bottom: true
    rotations_and_offsets:
      - field: Reference
        regex: U103
        angle: 180.0

Note that this rotation will have more precedence than the rotations database. Also note that you can eliminate the rotations database using the extends option. So you can create different filters with different mechanisms to adjust the rotations and offsets for different manufacturers.

Note that after matching a rule in the rotations_and_offsets list the values are applied and no farther search is done for this component. If you need to adjust the rotation, but keep searching for an offset, you can use the apply_angle. The apply_offset option is the counterpart for the offset.

Using the schematic

If you want to have more metadata inside the project you can use fields to specify the rotations and offsets. The JLC-Plugin-for-KiCad implements corrections embedding the adjusts in the schematic fields. You can apply it using KiBot filters. The rot_fields and offset_fields options can be used to specify a list of field names that contains such adjusts. Note that you can create different filters for different manufacturers specifying different field names.

The way asjusts are applied is compatible with the JLC-Plugin-for-KiCad plugin. If you want to apply adjusts computed like in the rotations_and_offsets, rotations and offsets options just disable the bennymeg_mode option. Also note that JLC-Plugin-for-KiCad v3.1.0 computes the adjusts for the bottom layer in a way that isn’t compatible with JLCPCB, at least on november 2023.

Using the default options a rotation filter will look for fields named JLCPCB Rotation Offset and/or JLCRotOffset for rotations and JLCPCB Position Offset and/or JLCPosOffset for offsets. The search isn’t case sensitive, so using things like jlcrotoffset is accepted.

Including through-hole components

The default behavior is to only include SMD components in the position files. This is because in most cases is expensive to automatically solder them. But with some manufacturers offering simple assembly at no cost you could want to include them.

For a position output you just need to disable the only_smd option. When using the JLCPCB template you can just use:

kibot:
  version: 1

import:
  - file: JLCPCB
    definitions:
      _KIBOT_POS_ONLY_SMD: false

Including fiducials and similar stuff

Position files are intended to be used by pick and place machines. So things like reference position marks are excluded. You can include them enabling the include_virtual option. But this could include other things you don’t want. As an example we will show how to include fiducials, but exclude mounting holes:

kibot:
  version: 1

filters:
  - name: remove_extra
    comment: 'Remove mounting holes'
    type: generic
    exclude_any:
      - column: References
        regex: "^H\\d+"

outputs:
  - name: 'position'
    type: position
    dir: positiondir
    options:
      dnf_filter: remove_extra
      format: CSV   # CSV or ASCII format
      units: millimeters  # millimeters or inches
      separate_files_for_front_and_back: false
      only_smd: false
      include_virtual: true

Here the remove_extra filter removes references starting with H and followed by a number, which are used for mounting holes. You could also filter them using a regular expression for the footprint.

The following example will generate a positions file only with the fiducials:

kibot:
  version: 1

filters:
  - name: remove_extra
    comment: 'Keep only fiducials'
    type: generic
    include_only:
      - column: References
        regex: "^FID\\d+"

outputs:
  - name: 'position'
    type: position
    dir: positiondir
    options:
      dnf_filter: remove_extra
      format: CSV   # CSV or ASCII format
      units: millimeters  # millimeters or inches
      separate_files_for_front_and_back: false
      only_smd: false
      include_virtual: true

Note that this needs synchronized schematics and PCBs. This means that all important objects in the PCB must be related to a symbol in the schematic and that you must ask KiCad to sync both. In particular the above example assumes fiducials are also in the schematic.

If the PCB contains footprints not found in the schematic, like in the case of a KiKit panel, you’ll need KiBot 1.6.4 or newer.

XYRS files

XYRS files are just BoM files in CSV format that includes pick and place data (X position, Y position, Rotation and Side). You can generate them using the internal BoM generator (bom output). The following fields contains the needed information:

  • Footprint X

  • Footprint Y

  • Footprint Rot

  • Footprint Side

Additionally we support:

  • Footprint Type (SMD, THT, VIRTUAL)

  • Footprint X-Size

  • Footprint Y-Size

  • Footprint Populate

Important: These files doesn’t support manual panelization with repeated reference names, you’ll get the coordinates for just one component because this is a BoM.

Internal list of rotations

Footprint

Rotation

^(.*?_|V)?QFN-(16|20|24|28|40)(-|_|$)

270

^BatteryHolder_Keystone_1060_1x2032

-180

^Bosch_LGA-

90

^CP_EIA-

180

^CP_Elec_

180

^C_Elec_

180

^DFN-

270

^D_SOT-23

180

^Diodes_PowerDI3333-8

270

^ESP32-W

270

^HTSSOP-

270

^JST_GH_SM

180

^JST_PH_S

180

^LED_WS2812B-2020_PLCC4_2.0x2.0mm

90

^LED_WS2812B_PLCC4

180

^LQFP-

270

^MSOP-

270

^PUIAudio_SMT_0825_S_4_R*

270

^PinHeader_2x03_P1\.27mm_Vertical

-90

^PinHeader_2x05_P1\.27mm_Vertical

-90

^PowerPAK_SO-8_Single

270

^QFN-

270

^Quectel_L80-R

270

^RP2040-QFN-56

270

^R_Array_Concave_

90

^R_Array_Convex_

90

^Relay_DPDT_Omron_G6K-2F-Y

270

^SC-74-6

180

^SO-

270

^SOIC-

270

^SOIC127P798X216-8N

270

^SOP-(?!(18_|4_))

270

^SOT-143

180

^SOT-223

180

^SOT-23

180

^SOT-353

180

^SOT-363

180

^SOT-89

180

^SSOP-

270

^SW_DIP_SPSTx01_Slide_Copal_CHS-01B_W7.62mm_P1.27mm

-180

^SW_SPST_B3

90

^TDSON-8-1

270

^TO-277

90

^TQFP-

270

^TSOP-6

270

^TSOT-23

180

^TSSOP-

270

^Transformer_Ethernet_Pulse_HX0068ANL

270

^UDFN-10

270

^USB_C_Receptacle_HRO_TYPE-C-31-M-12*

180

^USON-10

270

^VSON-8_

270

^VSSOP-10_

270

^VSSOP-8_

180

^VSSOP-8_3.0x3.0mm_P0.65mm

270

Internal list of offsets

Footprint

Offset X

Offset Y

^PinHeader_2x03_P1\.27mm_Vertical

-1.27

-0.64

^PinHeader_2x05_P1\.27mm_Vertical

-2.54

-0.64

^USB_C_Receptacle_XKB_U262-16XN-4BVC11

0.00

-1.44