13 KiB
CPL rotation corrections — re-bba-rb
re-bba-rb-cpl.csv is generated from kicad-cli pcb export pos (raw KiCad
rotation values), then hand-corrected for the parts below before each
regeneration. This file exists because that correction step is easy to lose
silently — regenerating the CPL "the normal way" wipes it out, and there is
no automated check that would catch that. Read this before touching the CPL.
Why this exists
KiCad's per-footprint rotation is measured from an arbitrary reference baked
into whoever drew that footprint — it does not reliably match what JLCPCB's
placement machine expects. This is a well-known, generic problem (see
cpl_rotations_db.csv from the JLCKicadTools / kicad-jlcpcb-tools projects
for the community's attempt at a fix), but the generic community
correction table was tried on this board and found wrong — it claims
LQFP-: 270°, but on this exact board/toolchain pairing (KiCad 10 +
kicad-cli pcb export pos, not the dedicated plugin) the raw LQFP/QFN
rotation is already correct. The generic table's values are calibrated
against that plugin's own raw-rotation baseline, which is not the same
baseline kicad-cli's raw CSV export produces (different Y-axis handling
between the two tools). Do not reapply the community table wholesale to
this board. Every correction below was derived per-part from direct visual
verification against JLCPCB's placement-preview render, not from that table.
Rotation sign convention
The CPL uses JLCPCB's Y-up coordinate frame (kicad-cli's csv pos format
already negates Y from KiCad's native Y-down). In that frame, increasing
rotation angle is counter-clockwise (standard math convention). So when
the user reports "needs N degrees clockwise", the correction applied is
new = (old - N) % 360, not +N. Get this backwards and every fix
lands 2×N off instead of correct.
How to reapply — copy this exact dict, don't reconstruct it from memory
This is the complete, current correction set (all 16 parts). Copy-paste this block, don't retype it from the table below or from memory — the one time it was retyped from a section of prose instead of copied whole, U4 got silently dropped and shipped with a wrong rotation.
import csv
corrections = {
'U7': 270, # SOIC-8, confirmed 2026-07-31 (net of +90 hypothesis + 180 more) — same net correction as U10
'U10': 270, # SOIC-8, confirmed (net of +90 hypothesis + 180 more)
'U11': -90, # LQFP-64, confirmed 2026-07-31 ("90 degrees clockwise") — SUPERSEDES the earlier "all large chips are right" report, see below
'D1': -90, 'D4': -90, 'D5': -90, 'U12': -90, # SOT-23-6, reconstructed hypothesis
'U1': -180, # SOT-23-5, confirmed (two -90 corrections)
'U4': -90, # SOT-23-5, confirmed ("U4 is good")
'Q1': 180, 'Q2': 180, # SOT-23 bottom layer, confirmed "upside down"
'D2': -90, # custom footprint, confirmed "90 CW"
'D3': 180, # SMA diode, confirmed "180"
'C65': 180, # electrolytic cap, confirmed "180"
'U2': -90, # SOT-583-8, confirmed "90 CW"
'U3': 180, # custom SOT95P280X110-6N footprint, confirmed "180"
} # positive = CCW, negative = CW
with open('re-bba-rb-cpl.csv', newline='') as f:
rows = list(csv.DictReader(f))
for r in rows:
if r['Designator'] in corrections:
old = float(r['Rotation'])
new = (old + corrections[r['Designator']]) % 360
r['Rotation'] = f"{new:.6f}"
with open('re-bba-rb-cpl.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['Designator','Mid X','Mid Y','Layer','Rotation'])
writer.writeheader()
writer.writerows(rows)
If a new correction comes in from a placement-preview check, add it to this dict (and the table below), never work from a hand-copied subset.
When regenerating the CPL from scratch (e.g. after a reroute or component
move), re-run kicad-cli pcb export pos for fresh positions, then
reapply every correction below — don't just diff/copy the old file, since
positions can legitimately change while rotations still need the same
correction offset applied on top.
Confirmed correct as-is — apply NO correction
These are the only parts that take zero correction. Everything else in this section title's spirit but requiring a nonzero offset (like U4 below) lives in the corrections table, not here — filing U4 in this section previously (saying "confirmed good" without also flagging its required -90° delta) caused a real bug: a later reapplication pass built its correction dict from this section's prose and silently skipped U4, regenerating the CPL with U4 back at raw/wrong rotation. Don't repeat that — if a part needs any nonzero delta from raw, it belongs in the table below, full stop, even if that delta is "confirmed correct and needs no further correction beyond it."
- U8 (LQFP-48), U9 (QFN-48) — "all large chips are right" (user, direct placement-preview check). ⚠️ That single report also covered U11, and U11 later turned out to need −90° after all (direct check, 2026-07-31). So the "large chips are fine" observation is now known to be wrong for at least one of the three parts it covered, and U8/U9 rest entirely on that same weakened report. Treat both as unverified, not settled — re-check them in the placement preview before assembly. Do not cite "all large chips are right" as evidence again.
- D8, D9 (LED_0603, bottom layer) — verified 2026-07-31 by tracing the circuit rather than by eye, see "D8/D9 polarity" below. No correction.
- J2 (HR911105A magjack, rot=90° in the PCB file) — not a CPL-rotation question (J2 doesn't need per-part correction below, it's not in the CPL correction set), but its physical orientation was independently verified: the RJ45 signal pins (pins 1–8, footprint local +Y) map to absolute −X under the board's 90° placement rotation, which correctly faces the board's tongue/cable-exit edge. Pin 1 appearing to "face outside the board" in the placement render is expected, not a defect — the RJ45 contacts are always near the cable-insertion opening by construction.
Corrections applied (all currently in re-bba-rb-cpl.csv)
Each row: designator, package, raw→corrected, confidence.
| Ref | Package | Raw | Corrected | Confidence |
|---|---|---|---|---|
| U7 | SOIC-8 | 90° | 0° (net: +90° reconstructed hypothesis, then +180° more, user-confirmed 2026-07-31) | Confirmed. Note this landed on exactly the same net correction as U10 (+270°), which retroactively makes the two SOIC-8 parts consistent — the original bare "+90°" hypothesis was wrong for both, in the same direction and by the same amount |
| U11 | LQFP-64 | -90° | 180° | Confirmed ("90 degrees clockwise", 2026-07-31). Previously filed as needing NO correction on the strength of "all large chips are right" — that report was wrong for this part. See the note in the no-correction section above: U8/U9 inherit the doubt |
| U10 | SOIC-8 | 0° | 270° (net: +90° reconstructed hypothesis, then +180° more, user-confirmed) | Partially confirmed — the +90° hypothesis alone was WRONG/incomplete (needed another 180° on top). This means the SOIC "+90°" hypothesis should not be trusted for U7 either without its own direct check, since it's now proven wrong at least once. |
| U3 | custom SOT95P280X110-6N |
0° | 180° | Confirmed ("180°") |
| D1 | SOT-23-6 | 0° | 270° | Reconstructed hypothesis — not independently re-confirmed |
| D4 | SOT-23-6 | 180° | 90° | Reconstructed hypothesis — not independently re-confirmed |
| D5 | SOT-23-6 | 180° | 90° | Reconstructed hypothesis — not independently re-confirmed |
| U12 | SOT-23-6 | 180° | 90° | Reconstructed hypothesis — not independently re-confirmed |
| U1 | SOT-23-5 | 180° | 0° (two corrections: -90° then another -90°, user-confirmed both times) | Confirmed by direct user feedback, twice |
| U4 | SOT-23-5 | 0° | 270° (-90°, the same family correction as D1/D4/D5/U12) | Confirmed — user checked and said "U4 is good" after this delta, no second correction needed (unlike U1) |
| Q1 | SOT-23 (bottom layer) | -90° | 90° | Confirmed ("upside down" = 180°, direction-agnostic) |
| Q2 | SOT-23 (bottom layer) | -90° | 90° | Confirmed (same as Q1) |
| D2 | custom SON50P110X213X50-7 |
0° | 270° | Confirmed ("90° clockwise") |
| D3 | Diode_SMD:D_SMA |
180° | 0° | Confirmed ("180°", direction-agnostic) |
| C65 | Capacitor_SMD:CP_Elec_6.3x7.7 |
0° | 180° | Confirmed ("180°", direction-agnostic) |
| U2 | SOT-583-8 | 180° | 90° | Confirmed ("90° clockwise") |
"Reconstructed hypothesis" parts (U7, U10, D1, D4, D5, U12) explained: the user reported "SOIC and SOT chips are rotated 90°, direction unsure." Cross-referencing against earlier feedback in the same session — where a -90° correction had been applied to the top-layer SOT-23 parts and NOT flagged as wrong, while the same -90° applied to a SOIC part (U10) WAS flagged wrong — implies SOT-23 needs -90° and SOIC needs the opposite, +90°. This is indirect reasoning, not a fresh direct check, and U1 later proved that even within one confirmed-wrong-by-90°-family, the correction does NOT reliably generalize per package type (U1 and U4 are both SOT-23-5, both got the -90° family correction, but U1 needed a second -90° on top while U4 was already correct) — so treat every "reconstructed hypothesis" row above as worth a fresh placement-preview check, not settled fact, despite no contradicting feedback having come in since.
D8/D9 polarity — checked by circuit tracing, NOT by eye (2026-07-31)
D8 was reported as possibly upside down because "it connects with its negative side to Q1". That observation is correct behaviour, not a fault — D8's cathode is supposed to face Q1. Tracing the net:
D8: 3V3 → R41 → D8.pad2 (A) ─ D8.pad1 (K) → Q1.pad3 (C) → Q1.pad2 (E) → GND
Q1 is an NPN low-side switch, base Q1.pad1 ← R39.
Current can only flow if the LED cathode is the Q1 end. ✔
D9: 3V3 → Q2.pad2 (E) → Q2.pad3 (C) → R42 → D9.pad2 (A) ─ D9.pad1 (K) → GND
Q2 is a PNP high-side switch, base Q2.pad1 ← R40.
D9's cathode goes straight to GND; it does NOT touch Q2 directly. ✔
Both are LED_SMD:LED_0603_1608Metric on B.Cu, both at raw −90°, and
both have pad 1 = cathode (the footprint's B.Fab chamfer marks the pad-1 /
cathode end). Absolute pad geometry, with the back-layer X-mirror applied:
| pad 1 (K) | pad 2 (A) | anode's partner | |
|---|---|---|---|
| D8 | (167.420, 47.300) | (167.420, 48.875) | R41 pad 2 at (166.170, 48.875) |
| D9 | (164.940, 47.340) | (164.940, 48.915) | R42 pad 2 at (163.690, 48.917) |
The anode pads land at the same Y as their series resistor's pad — that alignment is what confirms the back-layer mirror was applied correctly when computing these (get the mirror wrong and pad 1/2 swap, inverting the whole conclusion). Q1's collector pad sits at (165.350, 45.580), i.e. above D8 in Y — the same side as D8's cathode. So "cathode faces Q1" is exactly right.
Both D8 and D9 are oriented identically (cathode toward smaller Y). That is a useful invariant: they are the same part in the same circuit role, so if a future preview shows one of them needing a flip, both need it — a correction applied to only one of the pair is a mistake.
Note this relationship is mirror-invariant: whether the assembler renders the bottom layer viewed-from-below or seen-through-the-board, "D8's cathode end is the end nearer Q1" stays true. So it is a safe thing to eyeball in a preview, unlike absolute left/right or up/down judgements on bottom-layer parts.
Parts never checked at all
Every 0402/0603/0805 passive (R/C/L) except C65, J1/J3/J4 connectors, and anything else not listed in the corrections table above is sitting at raw, unverified KiCad rotation in the CPL. Passives in symmetric packages are usually fine raw (this matched observed behavior for the ones implicitly checked), but this has not been confirmed part-by-part the way the table above was.
Bottom line for whoever picks this up next
Before ordering assembly, re-check the entire board in JLCPCB's (or whatever assembler's) placement preview — this file only covers what's been individually verified so far, not a general guarantee the CPL is correct.