adding libraries
This commit is contained in:
0
footprints/footprints.pretty/CONN_BM05B-PASS-1_JST.kicad_mod → footprints/um-footprints.pretty/CONN_BM05B-PASS-1_JST.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/CONN_BM05B-PASS-1_JST.kicad_mod → footprints/um-footprints.pretty/CONN_BM05B-PASS-1_JST.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/LQFP100_14X14_NXP.kicad_mod → footprints/um-footprints.pretty/LQFP100_14X14_NXP.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/LQFP100_14X14_NXP.kicad_mod → footprints/um-footprints.pretty/LQFP100_14X14_NXP.kicad_mod
Executable file → Normal file
1839
footprints/um-footprints.pretty/MXA20A_TEX.kicad_mod
Normal file
1839
footprints/um-footprints.pretty/MXA20A_TEX.kicad_mod
Normal file
File diff suppressed because it is too large
Load Diff
0
footprints/footprints.pretty/QFN16_MG_MCH.kicad_mod → footprints/um-footprints.pretty/QFN16_MG_MCH.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/QFN16_MG_MCH.kicad_mod → footprints/um-footprints.pretty/QFN16_MG_MCH.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/SO16_TOS.kicad_mod → footprints/um-footprints.pretty/SO16_TOS.kicad_mod
Executable file → Normal file
0
footprints/footprints.pretty/SO16_TOS.kicad_mod → footprints/um-footprints.pretty/SO16_TOS.kicad_mod
Executable file → Normal file
94
libs.txt
Normal file
94
libs.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
Batteries
|
||||
BGA
|
||||
Capacitor
|
||||
Cap_0402
|
||||
Cap_0603
|
||||
Cap_0805
|
||||
Cap_1206
|
||||
Cap_Alum_SMD
|
||||
Cap_Alum_TH
|
||||
Cap_Array
|
||||
Cap_Film
|
||||
Cap_Misc
|
||||
Cap_Precision
|
||||
Cap_RF
|
||||
Cap_Tantalum
|
||||
Connector
|
||||
Connectors
|
||||
Conn_0-50mm
|
||||
Conn_1-00mm
|
||||
Conn_1-25mm
|
||||
Conn_1-27mm
|
||||
Conn_2-00mm
|
||||
Conn_2-00mm_DF11
|
||||
Conn_2-00mm_DF3
|
||||
Conn_2-54mm
|
||||
Conn_DF11
|
||||
Conn_Misc
|
||||
Conn_RF
|
||||
Conn_Test
|
||||
default
|
||||
DFN
|
||||
Diode
|
||||
DIP
|
||||
Disc_BJT
|
||||
Disc_Diode
|
||||
Disc_FET
|
||||
Disc_Zener_TVS
|
||||
Doc_sch
|
||||
Footprints
|
||||
Fuse
|
||||
Fuses
|
||||
IC_ADC
|
||||
IC_Analog
|
||||
IC_Audio
|
||||
IC_DAC
|
||||
IC_Driver
|
||||
IC_Interface
|
||||
IC_Logic
|
||||
IC_MCU
|
||||
IC_Memory
|
||||
IC_Misc
|
||||
IC_OpAmp
|
||||
IC_Power
|
||||
IC_Power_Linear
|
||||
IC_Power_SMPS
|
||||
IC_Power_SW_Cap
|
||||
IC_RF
|
||||
IC_Sensor
|
||||
Inductor
|
||||
Inductor_0402
|
||||
Inductor_SMD
|
||||
Inductor_TH
|
||||
Info
|
||||
LCD
|
||||
LED
|
||||
Library_Toolkit
|
||||
Mech
|
||||
Obsolete
|
||||
Opto
|
||||
PCB-Archive
|
||||
pcb-doc
|
||||
PCB
|
||||
QFN
|
||||
Relay
|
||||
Resistor
|
||||
Res_0402
|
||||
Res_0603
|
||||
Res_0805
|
||||
Res_1206
|
||||
Res_Array
|
||||
Res_Misc
|
||||
RF
|
||||
RF_Module
|
||||
Sensor
|
||||
SMT
|
||||
SOT
|
||||
SQFP
|
||||
Switch
|
||||
Symbol
|
||||
Test
|
||||
Tools
|
||||
Transistor
|
||||
VFD
|
||||
XTAL
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
BOM CSV Exporter
|
||||
================
|
||||
Reads extract_symbols.json and writes bom.csv with columns:
|
||||
Reference, MPN, MFG
|
||||
|
||||
Power symbols (#PWR, #FLG) are excluded.
|
||||
|
||||
Usage:
|
||||
python3 export_bom.py [project_dir]
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main(project_dir: Path):
|
||||
json_path = project_dir / 'extract_symbols.json'
|
||||
if not json_path.exists():
|
||||
print(f"Error: {json_path} not found. Run extract_symbols.py first.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
data = json.loads(json_path.read_text(encoding='utf-8'))
|
||||
symbols = data['symbols']
|
||||
|
||||
# Filter out power/flag symbols, DNP parts, and parts excluded from BOM
|
||||
parts = [
|
||||
s for s in symbols
|
||||
if not (s.get('reference') or '').startswith(('#', '~'))
|
||||
and not (s.get('lib_id') or '').startswith('power:')
|
||||
and s.get('in_bom') is not False
|
||||
and not s.get('dnp')
|
||||
]
|
||||
|
||||
# Collapse multi-unit / duplicate records to one row per reference.
|
||||
# If multiple records exist for the same ref, pick the one with the
|
||||
# most complete MPN/MFG data (longest non-placeholder string).
|
||||
def data_score(s):
|
||||
props = s.get('properties', {})
|
||||
mpn = props.get('MPN', '')
|
||||
mfg = props.get('MFG') or props.get('MANUFACTURER', '')
|
||||
placeholder = mpn.strip().lower() in ('', 'x', 'tbd', 'n/a', 'na')
|
||||
return (0 if placeholder else len(mpn) + len(mfg))
|
||||
|
||||
from collections import defaultdict
|
||||
by_ref: dict[str, list] = defaultdict(list)
|
||||
for s in parts:
|
||||
by_ref[s.get('reference', '')].append(s)
|
||||
|
||||
best: list[dict] = []
|
||||
for ref, recs in by_ref.items():
|
||||
best.append(max(recs, key=data_score))
|
||||
|
||||
# Sort by reference
|
||||
def ref_sort_key(r):
|
||||
ref = r.get('reference') or ''
|
||||
letters = ''.join(c for c in ref if c.isalpha())
|
||||
digits = ''.join(c for c in ref if c.isdigit())
|
||||
return (letters, int(digits) if digits else 0)
|
||||
|
||||
best.sort(key=ref_sort_key)
|
||||
|
||||
out_path = project_dir / 'bom.csv'
|
||||
with out_path.open('w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Reference', 'MPN', 'MFG'])
|
||||
for s in best:
|
||||
props = s.get('properties', {})
|
||||
writer.writerow([
|
||||
s.get('reference', ''),
|
||||
props.get('MPN', ''),
|
||||
props.get('MFG') or props.get('MANUFACTURER', ''),
|
||||
])
|
||||
|
||||
excluded_bom = sum(1 for s in symbols if s.get('in_bom') is False
|
||||
and not (s.get('reference') or '').startswith(('#', '~')))
|
||||
excluded_dnp = sum(1 for s in symbols if s.get('dnp')
|
||||
and not (s.get('reference') or '').startswith(('#', '~'))
|
||||
and s.get('in_bom') is not False)
|
||||
print(f"Excluded: {excluded_bom} 'exclude from BOM', {excluded_dnp} DNP")
|
||||
print(f"Wrote {len(best)} unique references to {out_path} (collapsed from {len(parts)} records)")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
project_dir = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else Path(__file__).parent.resolve()
|
||||
main(project_dir)
|
||||
@@ -1,517 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
KiCad 9 Symbol Metadata Extractor
|
||||
==================================
|
||||
Walks every .kicad_sch file in the project directory and extracts
|
||||
metadata for every placed symbol (component instance), correctly
|
||||
expanding hierarchical sheet instances so that each unique reference
|
||||
in the final design becomes its own record.
|
||||
|
||||
KiCad stores multi-instance sheets by embedding an `(instances ...)`
|
||||
block in each symbol. That block contains one `(path ...)` entry per
|
||||
sheet instantiation, each with the authoritative reference for that
|
||||
copy. This script reads those paths so a sheet used N times produces
|
||||
N distinct records per symbol.
|
||||
|
||||
Output: extract_symbols.json (same directory as this script)
|
||||
|
||||
Usage:
|
||||
python3 extract_symbols.py [project_dir]
|
||||
|
||||
If project_dir is omitted, the directory containing this script is used.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# S-expression parser
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _tokenize(text: str) -> list:
|
||||
"""
|
||||
Convert raw KiCad S-expression text into a flat list of tokens.
|
||||
Token forms:
|
||||
('OPEN',) – opening paren
|
||||
('CLOSE',) – closing paren
|
||||
('ATOM', value) – unquoted word / number / bool
|
||||
('STR', value) – double-quoted string (escapes resolved)
|
||||
"""
|
||||
tokens = []
|
||||
i, n = 0, len(text)
|
||||
while i < n:
|
||||
c = text[i]
|
||||
if c in ' \t\r\n':
|
||||
i += 1
|
||||
elif c == '(':
|
||||
tokens.append(('OPEN',))
|
||||
i += 1
|
||||
elif c == ')':
|
||||
tokens.append(('CLOSE',))
|
||||
i += 1
|
||||
elif c == '"':
|
||||
j = i + 1
|
||||
buf = []
|
||||
while j < n:
|
||||
if text[j] == '\\' and j + 1 < n:
|
||||
buf.append(text[j + 1])
|
||||
j += 2
|
||||
elif text[j] == '"':
|
||||
j += 1
|
||||
break
|
||||
else:
|
||||
buf.append(text[j])
|
||||
j += 1
|
||||
tokens.append(('STR', ''.join(buf)))
|
||||
i = j
|
||||
else:
|
||||
j = i
|
||||
while j < n and text[j] not in ' \t\r\n()':
|
||||
j += 1
|
||||
tokens.append(('ATOM', text[i:j]))
|
||||
i = j
|
||||
return tokens
|
||||
|
||||
|
||||
def _parse(tokens: list, pos: int) -> tuple:
|
||||
"""
|
||||
Recursively parse one S-expression value starting at *pos*.
|
||||
Returns (parsed_value, next_pos).
|
||||
A list/node becomes a Python list; atoms and strings become strings.
|
||||
"""
|
||||
tok = tokens[pos]
|
||||
kind = tok[0]
|
||||
if kind == 'OPEN':
|
||||
pos += 1
|
||||
items = []
|
||||
while tokens[pos][0] != 'CLOSE':
|
||||
item, pos = _parse(tokens, pos)
|
||||
items.append(item)
|
||||
return items, pos + 1 # consume CLOSE
|
||||
elif kind in ('ATOM', 'STR'):
|
||||
return tok[1], pos + 1
|
||||
else:
|
||||
raise ValueError(f"Unexpected token at pos {pos}: {tok}")
|
||||
|
||||
|
||||
def parse_sexp(text: str):
|
||||
"""Parse a complete KiCad S-expression file. Returns the root list."""
|
||||
tokens = _tokenize(text)
|
||||
root, _ = _parse(tokens, 0)
|
||||
return root
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers to navigate parsed S-expressions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def tag(node) -> str:
|
||||
if isinstance(node, list) and node and isinstance(node[0], str):
|
||||
return node[0]
|
||||
return ''
|
||||
|
||||
|
||||
def children(node: list) -> list:
|
||||
return node[1:] if isinstance(node, list) else []
|
||||
|
||||
|
||||
def first_child_with_tag(node: list, name: str):
|
||||
for child in children(node):
|
||||
if isinstance(child, list) and tag(child) == name:
|
||||
return child
|
||||
return None
|
||||
|
||||
|
||||
def all_children_with_tag(node: list, name: str) -> list:
|
||||
return [c for c in children(node) if isinstance(c, list) and tag(c) == name]
|
||||
|
||||
|
||||
def scalar(node, index: int = 1, default=None):
|
||||
if isinstance(node, list) and len(node) > index:
|
||||
return node[index]
|
||||
return default
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Instance path extraction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def extract_instances(sym_node: list) -> list[dict]:
|
||||
"""
|
||||
Parse the (instances ...) block of a symbol and return one dict per
|
||||
hierarchical path. Each dict has:
|
||||
path – the full UUID path string
|
||||
reference – the reference designator for that instance
|
||||
unit – the unit number for that instance
|
||||
project – the project name
|
||||
|
||||
If there is no instances block (unusual), returns an empty list.
|
||||
"""
|
||||
instances_node = first_child_with_tag(sym_node, 'instances')
|
||||
if instances_node is None:
|
||||
return []
|
||||
|
||||
results = []
|
||||
for project_node in all_children_with_tag(instances_node, 'project'):
|
||||
project_name = scalar(project_node, 1, '')
|
||||
for path_node in all_children_with_tag(project_node, 'path'):
|
||||
path_str = scalar(path_node, 1, '')
|
||||
ref_node = first_child_with_tag(path_node, 'reference')
|
||||
unit_node = first_child_with_tag(path_node, 'unit')
|
||||
results.append({
|
||||
'path': path_str,
|
||||
'reference': scalar(ref_node, 1) if ref_node else None,
|
||||
'unit': scalar(unit_node, 1) if unit_node else None,
|
||||
'project': project_name,
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Symbol extraction
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def extract_symbol_records(sym_node: list, sheet_file: str) -> list[dict]:
|
||||
"""
|
||||
Extract metadata from a placed-symbol node and return one record per
|
||||
hierarchical instance (i.e. one record per path in the instances block).
|
||||
|
||||
For a sheet used only once, this produces a single record.
|
||||
For a sheet instantiated N times, this produces N records — each with
|
||||
its own unique reference designator from the instances block.
|
||||
"""
|
||||
# --- Shared fields (same for all instances of this symbol placement) ---
|
||||
shared = {
|
||||
'sheet_file': sheet_file,
|
||||
'lib_id': None,
|
||||
'at': None,
|
||||
'exclude_from_sim': None,
|
||||
'in_bom': None,
|
||||
'on_board': None,
|
||||
'dnp': None,
|
||||
'uuid': None,
|
||||
'properties': {},
|
||||
}
|
||||
|
||||
for child in children(sym_node):
|
||||
if not isinstance(child, list):
|
||||
continue
|
||||
t = tag(child)
|
||||
if t == 'lib_id':
|
||||
shared['lib_id'] = scalar(child, 1)
|
||||
elif t == 'at':
|
||||
shared['at'] = {
|
||||
'x': scalar(child, 1),
|
||||
'y': scalar(child, 2),
|
||||
'angle': scalar(child, 3, 0),
|
||||
}
|
||||
elif t == 'exclude_from_sim':
|
||||
shared['exclude_from_sim'] = scalar(child, 1) == 'yes'
|
||||
elif t == 'in_bom':
|
||||
shared['in_bom'] = scalar(child, 1) == 'yes'
|
||||
elif t == 'on_board':
|
||||
shared['on_board'] = scalar(child, 1) == 'yes'
|
||||
elif t == 'dnp':
|
||||
shared['dnp'] = scalar(child, 1) == 'yes'
|
||||
elif t == 'uuid':
|
||||
shared['uuid'] = scalar(child, 1)
|
||||
elif t == 'property':
|
||||
prop_name = scalar(child, 1)
|
||||
prop_val = scalar(child, 2)
|
||||
if prop_name is not None:
|
||||
shared['properties'][prop_name] = prop_val
|
||||
|
||||
# Promote standard properties for convenient access
|
||||
props = shared['properties']
|
||||
shared['value'] = props.get('Value')
|
||||
shared['footprint'] = props.get('Footprint')
|
||||
shared['datasheet'] = props.get('Datasheet')
|
||||
shared['description'] = props.get('Description')
|
||||
|
||||
# --- Per-instance fields (one record per path in instances block) ---
|
||||
instances = extract_instances(sym_node)
|
||||
|
||||
if not instances:
|
||||
# Fallback: no instances block — use top-level Reference property
|
||||
record = dict(shared)
|
||||
record['reference'] = props.get('Reference')
|
||||
record['instance_path'] = None
|
||||
record['instance_unit'] = shared.get('unit')
|
||||
record['instance_project']= None
|
||||
return [record]
|
||||
|
||||
records = []
|
||||
for inst in instances:
|
||||
record = dict(shared)
|
||||
record['properties'] = dict(shared['properties']) # copy so each is independent
|
||||
record['reference'] = inst['reference']
|
||||
record['instance_path'] = inst['path']
|
||||
record['instance_unit'] = inst['unit']
|
||||
record['instance_project'] = inst['project']
|
||||
records.append(record)
|
||||
|
||||
return records
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hierarchy walker
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def find_reachable_sheets(root_sch: Path) -> list[Path]:
|
||||
"""
|
||||
Walk the sheet hierarchy starting from *root_sch* and return an ordered
|
||||
list of every .kicad_sch file that is actually reachable (i.e. referenced
|
||||
directly or transitively as a sub-sheet). Handles repeated sub-sheet
|
||||
references (same file used N times) by visiting the file only once.
|
||||
"""
|
||||
reachable: list[Path] = []
|
||||
visited_names: set[str] = set()
|
||||
queue: list[Path] = [root_sch]
|
||||
|
||||
while queue:
|
||||
sch = queue.pop(0)
|
||||
if sch.name in visited_names:
|
||||
continue
|
||||
visited_names.add(sch.name)
|
||||
reachable.append(sch)
|
||||
|
||||
try:
|
||||
text = sch.read_text(encoding='utf-8')
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
root_node = parse_sexp(text)
|
||||
for child in children(root_node):
|
||||
if tag(child) != 'sheet':
|
||||
continue
|
||||
for prop in all_children_with_tag(child, 'property'):
|
||||
if scalar(prop, 1) == 'Sheetfile':
|
||||
child_filename = scalar(prop, 2)
|
||||
if child_filename:
|
||||
child_path = sch.parent / child_filename
|
||||
if child_path.exists() and child_path.name not in visited_names:
|
||||
queue.append(child_path)
|
||||
|
||||
return reachable
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Per-file parsing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def extract_from_schematic(sch_path: Path) -> list[dict]:
|
||||
"""
|
||||
Parse one .kicad_sch file and return a list of symbol records.
|
||||
lib_symbols definitions are skipped; only placed instances are returned.
|
||||
"""
|
||||
text = sch_path.read_text(encoding='utf-8')
|
||||
root = parse_sexp(text)
|
||||
|
||||
results = []
|
||||
for child in children(root):
|
||||
if not isinstance(child, list):
|
||||
continue
|
||||
t = tag(child)
|
||||
if t == 'lib_symbols':
|
||||
continue # skip library definitions
|
||||
if t == 'symbol' and first_child_with_tag(child, 'lib_id') is not None:
|
||||
records = extract_symbol_records(child, sch_path.name)
|
||||
results.extend(records)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def get_root_uuid(project_dir: Path) -> str | None:
|
||||
"""
|
||||
Find the UUID of the root schematic by reading the .kicad_pro file
|
||||
(which names the root sheet) or by scanning for the top-level sheet.
|
||||
Returns the UUID string, or None if it cannot be determined.
|
||||
"""
|
||||
# The .kicad_pro file tells us the root schematic filename
|
||||
pro_files = list(project_dir.glob('*.kicad_pro'))
|
||||
root_sch: Path | None = None
|
||||
|
||||
if pro_files:
|
||||
import json as _json
|
||||
try:
|
||||
pro = _json.loads(pro_files[0].read_text(encoding='utf-8'))
|
||||
root_name = pro.get('sheets', [{}])[0] if pro.get('sheets') else None
|
||||
# Fall back: just find a .kicad_sch with the same stem as the .pro
|
||||
root_sch = project_dir / (pro_files[0].stem + '.kicad_sch')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if root_sch is None or not root_sch.exists():
|
||||
# Guess: the .kicad_sch whose stem matches the .kicad_pro
|
||||
if pro_files:
|
||||
candidate = project_dir / (pro_files[0].stem + '.kicad_sch')
|
||||
if candidate.exists():
|
||||
root_sch = candidate
|
||||
|
||||
if root_sch is None or not root_sch.exists():
|
||||
return None
|
||||
|
||||
# Extract the first (uuid ...) at the root level of the file
|
||||
import re
|
||||
text = root_sch.read_text(encoding='utf-8')
|
||||
m = re.search(r'\(uuid\s+"([^"]+)"', text)
|
||||
return m.group(1) if m else None
|
||||
|
||||
|
||||
def main(project_dir: Path):
|
||||
# Determine root schematic and walk the real hierarchy
|
||||
root_uuid = get_root_uuid(project_dir)
|
||||
|
||||
pro_files = list(project_dir.glob('*.kicad_pro'))
|
||||
root_sch = project_dir / (pro_files[0].stem + '.kicad_sch') if pro_files else None
|
||||
|
||||
if root_sch and root_sch.exists():
|
||||
sch_files = find_reachable_sheets(root_sch)
|
||||
print(f"Root sheet: {root_sch.name}")
|
||||
print(f"Found {len(sch_files)} reachable schematic file(s) in hierarchy:")
|
||||
else:
|
||||
# Fallback: glob everything
|
||||
sch_files = sorted(
|
||||
p for p in project_dir.rglob('*.kicad_sch')
|
||||
if not p.name.startswith('_autosave')
|
||||
and not p.suffix.endswith('.bak')
|
||||
)
|
||||
print(f"Warning: could not find root schematic; scanning all {len(sch_files)} files.\n")
|
||||
|
||||
if not sch_files:
|
||||
print(f"No .kicad_sch files found in {project_dir}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
for f in sch_files:
|
||||
print(f" {f.relative_to(project_dir)}")
|
||||
|
||||
all_records: list[dict] = []
|
||||
|
||||
for sch_path in sch_files:
|
||||
print(f"\nParsing {sch_path.name} ...", end=' ', flush=True)
|
||||
records = extract_from_schematic(sch_path)
|
||||
print(f"{len(records)} instance record(s)")
|
||||
all_records.extend(records)
|
||||
|
||||
# All records come from reachable sheets, so no orphan filtering needed.
|
||||
# Optionally still filter by root UUID to catch stale instance paths.
|
||||
if root_uuid:
|
||||
active_prefix = f'/{root_uuid}/'
|
||||
active = [r for r in all_records
|
||||
if (r.get('instance_path') or '').startswith(active_prefix)]
|
||||
stale = len(all_records) - len(active)
|
||||
print(f"\nTotal records : {len(all_records)}")
|
||||
if stale:
|
||||
print(f"Stale paths dropped: {stale}")
|
||||
else:
|
||||
active = all_records
|
||||
print(f"\nTotal records: {len(all_records)}")
|
||||
|
||||
# ---- Stage 1: dedup by (instance_path, uuid) ----
|
||||
# Collapses records that were seen from multiple sheet scans into one.
|
||||
seen: set = set()
|
||||
stage1: list[dict] = []
|
||||
for r in active:
|
||||
key = (r.get('instance_path'), r.get('uuid'))
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
stage1.append(r)
|
||||
|
||||
# ---- Stage 2: dedup by uuid across different sheet files ----
|
||||
# If the SAME uuid appears in two *different* .kicad_sch files, that is a
|
||||
# UUID collision in the design (copy-paste without UUID regeneration).
|
||||
# The same uuid appearing in the same sheet file with different instance
|
||||
# paths is *correct* — it is how multi-instance sheets work, so those are
|
||||
# left alone.
|
||||
uuid_sheets: dict = {} # uuid -> set of sheet_files seen
|
||||
uuid_collisions: dict = {} # uuid -> list of colliding records
|
||||
unique: list[dict] = []
|
||||
for r in stage1:
|
||||
u = r.get('uuid')
|
||||
sf = r.get('sheet_file', '')
|
||||
sheets_so_far = uuid_sheets.setdefault(u, set())
|
||||
if not sheets_so_far or sf in sheets_so_far:
|
||||
# First time seeing this uuid, OR it's from the same sheet file
|
||||
# (legitimate multi-instance expansion) — keep it.
|
||||
sheets_so_far.add(sf)
|
||||
unique.append(r)
|
||||
else:
|
||||
# Same uuid, but from a DIFFERENT sheet file → UUID collision.
|
||||
uuid_collisions.setdefault(u, []).append(r)
|
||||
# Don't append to unique — drop the duplicate.
|
||||
|
||||
if uuid_collisions:
|
||||
print(f"\nNote: {len(uuid_collisions)} UUID collision(s) detected "
|
||||
f"(same symbol UUID in multiple sheet files — likely copy-paste artifacts).")
|
||||
print(" Only the first occurrence is kept in the output.")
|
||||
for u, recs in list(uuid_collisions.items())[:10]:
|
||||
refs = [r.get('reference') for r in recs]
|
||||
files = [r.get('sheet_file') for r in recs]
|
||||
print(f" uuid={u[:8]}... refs={refs} sheets={files}")
|
||||
|
||||
print(f"\nUnique instances after dedup: {len(unique)}")
|
||||
|
||||
# Separate power symbols from real parts
|
||||
real = [r for r in unique if not (r.get('lib_id') or '').startswith('power:')]
|
||||
power = [r for r in unique if (r.get('lib_id') or '').startswith('power:')]
|
||||
print(f" Non-power parts : {len(real)}")
|
||||
print(f" Power symbols : {len(power)}")
|
||||
|
||||
# Check for true reference duplicates (same ref, different uuid = multi-unit)
|
||||
from collections import defaultdict, Counter
|
||||
by_ref: dict[str, list] = defaultdict(list)
|
||||
for r in unique:
|
||||
by_ref[r.get('reference', '')].append(r)
|
||||
|
||||
multi_unit = {ref: recs for ref, recs in by_ref.items()
|
||||
if len(recs) > 1 and len({r['uuid'] for r in recs}) > 1}
|
||||
if multi_unit:
|
||||
refs = [r for r in multi_unit if not r.startswith('#')]
|
||||
if refs:
|
||||
print(f"\nMulti-unit components ({len(refs)} references, expected for split-unit symbols):")
|
||||
for ref in sorted(refs):
|
||||
units = [r['instance_unit'] for r in multi_unit[ref]]
|
||||
print(f" {ref}: units {units}")
|
||||
|
||||
output = {
|
||||
"project_dir": str(project_dir),
|
||||
"root_uuid": root_uuid,
|
||||
"schematic_files": [str(f.relative_to(project_dir)) for f in sch_files],
|
||||
"total_instances": len(unique),
|
||||
"non_power_count": len(real),
|
||||
"symbols": unique,
|
||||
}
|
||||
|
||||
out_path = project_dir / 'extract_symbols.json'
|
||||
out_path.write_text(json.dumps(output, indent=2, ensure_ascii=False), encoding='utf-8')
|
||||
print(f"\nOutput written to: {out_path}")
|
||||
|
||||
# Print a summary table
|
||||
print("\n--- Summary (non-power parts, sorted by reference) ---")
|
||||
for r in sorted(real, key=lambda x: x.get('reference') or ''):
|
||||
ref = r.get('reference', '')
|
||||
value = r.get('value', '')
|
||||
lib = r.get('lib_id', '')
|
||||
mpn = r['properties'].get('MPN', '')
|
||||
sheet = r.get('sheet_file', '')
|
||||
unit = r.get('instance_unit', '')
|
||||
print(f" {ref:<12} u{unit:<2} {value:<30} {lib:<40} MPN={mpn:<25} [{sheet}]")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
project_dir = Path(sys.argv[1]).resolve()
|
||||
else:
|
||||
project_dir = Path(__file__).parent.resolve()
|
||||
|
||||
if not project_dir.is_dir():
|
||||
print(f"Error: {project_dir} is not a directory", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
main(project_dir)
|
||||
@@ -1,338 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
gen_resistors_0402.py
|
||||
=====================
|
||||
Reads the approved parts list spreadsheet and generates KiCad 9 symbols
|
||||
for every 0402 resistor into kicad-lib/symbols/Res_0402.kicad_sym.
|
||||
|
||||
The existing R_temp template symbol is kept in the file. One new symbol
|
||||
is added per unique value+tolerance combination, named descriptively, e.g.:
|
||||
R0402_4.7K_1%
|
||||
R0402_100_5%
|
||||
R0402_10.0K_0.1%
|
||||
|
||||
Symbol fields:
|
||||
Value ← Value1 (e.g. "10k", "4.7k", "100")
|
||||
Description ← Description column
|
||||
UMPN ← GLE P/N (internal part number of first approved vendor)
|
||||
MFG ← Mfg.1
|
||||
MPN ← Mfg.1 P/N
|
||||
|
||||
Where multiple approved vendors share the same value+tolerance, only the
|
||||
first row is used (the duplicate rows are reported and skipped).
|
||||
|
||||
All geometry and pin definitions are copied verbatim from R_temp.
|
||||
|
||||
Usage:
|
||||
python3 gen_resistors_0402.py <parts_list.xlsx> <Res_0402.kicad_sym>
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def kicad_str(value: str) -> str:
|
||||
"""Escape a string for use inside a KiCad S-expression quoted string."""
|
||||
return str(value).replace('\\', '\\\\').replace('"', '\\"')
|
||||
|
||||
|
||||
def make_symbol_name(value: str, description: str) -> str:
|
||||
"""
|
||||
Build a descriptive KiCad symbol name, e.g. 'R0402_4.7K_1%'.
|
||||
|
||||
value – the Value1 column (e.g. '4.7k', '100', '10.0k')
|
||||
description – the Description column (e.g. 'Resistor, 0402, 1%, 4.7k')
|
||||
|
||||
The value suffix (k, m, g) is uppercased. Tolerance is extracted
|
||||
with a regex that matches patterns like '1%', '0.1%', '5%'.
|
||||
"""
|
||||
# Uppercase trailing unit letter(s): 4.7k → 4.7K, 10.0k → 10.0K
|
||||
value_norm = re.sub(r'([a-zA-Z]+)$', lambda m: m.group(1).upper(), value.strip())
|
||||
# Extract tolerance from description
|
||||
tol_match = re.search(r'(\d+(?:\.\d+)?%)', description)
|
||||
tolerance = tol_match.group(1) if tol_match else 'X'
|
||||
return f'R0402_{value_norm}_{tolerance}'
|
||||
|
||||
|
||||
def make_symbol(name: str, value: str, description: str,
|
||||
umpn: str, mfg: str, mpn: str,
|
||||
geometry_body: str) -> str:
|
||||
"""
|
||||
Render a complete (symbol ...) block.
|
||||
|
||||
geometry_body is the text of the two inner sub-symbols
|
||||
(originally named R_temp_0_1 and R_temp_1_1) with the name
|
||||
already substituted.
|
||||
"""
|
||||
return f'''\t(symbol "{kicad_str(name)}"
|
||||
\t\t(pin_numbers
|
||||
\t\t\t(hide yes)
|
||||
\t\t)
|
||||
\t\t(pin_names
|
||||
\t\t\t(offset 0)
|
||||
\t\t)
|
||||
\t\t(exclude_from_sim no)
|
||||
\t\t(in_bom yes)
|
||||
\t\t(on_board yes)
|
||||
\t\t(property "Reference" "R"
|
||||
\t\t\t(at 2.54 0 90)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "Value" "{kicad_str(value)}"
|
||||
\t\t\t(at -2.54 0 90)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "Footprint" "Resistor_SMD:R_0402_1005Metric"
|
||||
\t\t\t(at 1.016 -0.254 90)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "Datasheet" "~"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "Description" "{kicad_str(description)}"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "MPN" "{kicad_str(mpn)}"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "MFG" "{kicad_str(mfg)}"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "UMPN" "{kicad_str(umpn)}"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "ki_keywords" "R res resistor"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
\t\t(property "ki_fp_filters" "R_*"
|
||||
\t\t\t(at 0 0 0)
|
||||
\t\t\t(effects
|
||||
\t\t\t\t(font
|
||||
\t\t\t\t\t(size 1.27 1.27)
|
||||
\t\t\t\t)
|
||||
\t\t\t\t(hide yes)
|
||||
\t\t\t)
|
||||
\t\t)
|
||||
{geometry_body}
|
||||
\t\t(embedded_fonts no)
|
||||
\t)'''
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Extract geometry from R_temp
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def extract_geometry(sym_text: str) -> str:
|
||||
"""
|
||||
Pull out the two inner sub-symbol blocks (R_temp_0_1 and R_temp_1_1)
|
||||
from the raw file text and return them as a single string with the
|
||||
'R_temp' name prefix replaced by a placeholder that callers substitute.
|
||||
"""
|
||||
# Match (symbol "R_temp_0_1" ...) and (symbol "R_temp_1_1" ...)
|
||||
# by tracking brace depth after the opening paren of each sub-symbol.
|
||||
results = []
|
||||
for sub in ('R_temp_0_1', 'R_temp_1_1'):
|
||||
pattern = f'(symbol "{sub}"'
|
||||
start = sym_text.find(pattern)
|
||||
if start == -1:
|
||||
raise ValueError(f"Could not find sub-symbol '{sub}' in template")
|
||||
# Walk forward to find the matching closing paren
|
||||
depth = 0
|
||||
i = start
|
||||
while i < len(sym_text):
|
||||
if sym_text[i] == '(':
|
||||
depth += 1
|
||||
elif sym_text[i] == ')':
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
results.append(sym_text[start:i + 1])
|
||||
break
|
||||
i += 1
|
||||
return '\n'.join(results)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main(xlsx_path: Path, sym_path: Path):
|
||||
# ---- Load spreadsheet ----
|
||||
df = pd.read_excel(xlsx_path, sheet_name='PCB', dtype=str)
|
||||
df = df.fillna('')
|
||||
|
||||
# Filter to 0402 resistors
|
||||
mask = (
|
||||
df['Footprint'].str.contains('0402', na=False) &
|
||||
df['Description'].str.contains('[Rr]es', na=False, regex=True)
|
||||
)
|
||||
resistors = df[mask].copy()
|
||||
print(f"Found {len(resistors)} 0402 resistors in parts list")
|
||||
|
||||
# ---- Read existing symbol file ----
|
||||
existing = sym_path.read_text(encoding='utf-8')
|
||||
|
||||
# Extract geometry from R_temp once
|
||||
raw_geom = extract_geometry(existing)
|
||||
|
||||
# ---- Build new symbols ----
|
||||
new_symbols = []
|
||||
skipped = []
|
||||
seen_names: dict[str, str] = {} # sym_name → GLE P/N of first occurrence
|
||||
|
||||
for _, row in resistors.iterrows():
|
||||
gle_pn = str(row['GLE P/N']).strip()
|
||||
value = str(row['Value1']).strip()
|
||||
description = str(row['Description']).strip()
|
||||
mfg = str(row['Mfg.1']).strip()
|
||||
mpn = str(row['Mfg.1 P/N']).strip()
|
||||
|
||||
if not gle_pn:
|
||||
skipped.append(('(no GLE P/N)', value))
|
||||
continue
|
||||
|
||||
# Build descriptive symbol name, e.g. "R0402_4.7K_1%"
|
||||
sym_name = make_symbol_name(value, description)
|
||||
|
||||
# Skip duplicate value+tolerance combinations (alternate approved vendors)
|
||||
if sym_name in seen_names:
|
||||
skipped.append((sym_name, f'dup of GLE {seen_names[sym_name]} (this: {gle_pn})'))
|
||||
continue
|
||||
seen_names[sym_name] = gle_pn
|
||||
|
||||
# Substitute R_temp → this symbol's name in the geometry block.
|
||||
# Also re-indent so sub-symbol opening parens align with the template.
|
||||
geom = raw_geom.replace('R_temp_0_1', f'{sym_name}_0_1') \
|
||||
.replace('R_temp_1_1', f'{sym_name}_1_1')
|
||||
# Each sub-symbol block starts at column 0 after extract; add two tabs.
|
||||
geom = '\n'.join('\t\t' + line if line.startswith('(symbol ') else line
|
||||
for line in geom.splitlines())
|
||||
|
||||
sym = make_symbol(
|
||||
name=sym_name,
|
||||
value=value,
|
||||
description=description,
|
||||
umpn=gle_pn,
|
||||
mfg=mfg,
|
||||
mpn=mpn,
|
||||
geometry_body=geom,
|
||||
)
|
||||
new_symbols.append(sym)
|
||||
|
||||
dups = [(n, r) for n, r in skipped if n != '(no GLE P/N)']
|
||||
no_pn = [(n, r) for n, r in skipped if n == '(no GLE P/N)']
|
||||
print(f"Generated {len(new_symbols)} symbols "
|
||||
f"({len(dups)} duplicate value/tol skipped, {len(no_pn)} missing GLE P/N)")
|
||||
if dups:
|
||||
print(" Skipped duplicates:")
|
||||
for name, reason in dups:
|
||||
print(f" {name}: {reason}")
|
||||
|
||||
# ---- Rebuild file from scratch (idempotent) ----
|
||||
# Extract the library header lines (everything before the first symbol)
|
||||
# and keep only the R_temp template symbol, then append generated symbols.
|
||||
header_end = existing.find('\n\t(symbol "R_temp"')
|
||||
if header_end == -1:
|
||||
raise ValueError("Could not find R_temp template in symbol file")
|
||||
header = existing[:header_end]
|
||||
|
||||
# Extract R_temp block by brace depth
|
||||
rt_start = existing.index('\n\t(symbol "R_temp"')
|
||||
depth, i = 0, rt_start + 1 # skip leading newline
|
||||
while i < len(existing):
|
||||
if existing[i] == '(':
|
||||
depth += 1
|
||||
elif existing[i] == ')':
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
rt_block = existing[rt_start:i + 1]
|
||||
break
|
||||
i += 1
|
||||
|
||||
lib_body = header + rt_block + '\n' + '\n'.join(new_symbols) + '\n)'
|
||||
|
||||
# Back up original
|
||||
backup = sym_path.with_suffix('.bak')
|
||||
backup.write_text(existing, encoding='utf-8')
|
||||
print(f"Backup written to {backup.name}")
|
||||
|
||||
sym_path.write_text(lib_body, encoding='utf-8')
|
||||
print(f"Wrote {sym_path}")
|
||||
|
||||
# Quick sanity check
|
||||
symbol_count = lib_body.count('\n\t(symbol "')
|
||||
print(f"Total symbols in file (including R_temp): {symbol_count}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) == 3:
|
||||
xlsx = Path(sys.argv[1])
|
||||
sym = Path(sys.argv[2])
|
||||
else:
|
||||
# Default paths relative to repo root
|
||||
repo = Path(__file__).parent.parent
|
||||
xlsx = repo.parent / 'parts_list_pcb.xlsx'
|
||||
sym = repo / 'symbols' / 'Res_0402.kicad_sym'
|
||||
|
||||
if not xlsx.exists():
|
||||
print(f"Error: spreadsheet not found at {xlsx}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if not sym.exists():
|
||||
print(f"Error: symbol file not found at {sym}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
main(xlsx, sym)
|
||||
5
symbols/BGA.kicad_sym
Normal file
5
symbols/BGA.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Batteries.kicad_sym
Normal file
5
symbols/Batteries.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_0402.kicad_sym
Normal file
5
symbols/Cap_0402.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_0603.kicad_sym
Normal file
5
symbols/Cap_0603.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_0805.kicad_sym
Normal file
5
symbols/Cap_0805.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_1206.kicad_sym
Normal file
5
symbols/Cap_1206.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Alum_SMD.kicad_sym
Normal file
5
symbols/Cap_Alum_SMD.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Alum_TH.kicad_sym
Normal file
5
symbols/Cap_Alum_TH.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Array.kicad_sym
Normal file
5
symbols/Cap_Array.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Film.kicad_sym
Normal file
5
symbols/Cap_Film.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Misc.kicad_sym
Normal file
5
symbols/Cap_Misc.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Precision.kicad_sym
Normal file
5
symbols/Cap_Precision.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_RF.kicad_sym
Normal file
5
symbols/Cap_RF.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Cap_Tantalum.kicad_sym
Normal file
5
symbols/Cap_Tantalum.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Capacitor.kicad_sym
Normal file
5
symbols/Capacitor.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_0-50mm.kicad_sym
Normal file
5
symbols/Conn_0-50mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_1-00mm.kicad_sym
Normal file
5
symbols/Conn_1-00mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_1-25mm.kicad_sym
Normal file
5
symbols/Conn_1-25mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_1-27mm.kicad_sym
Normal file
5
symbols/Conn_1-27mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_2-00mm.kicad_sym
Normal file
5
symbols/Conn_2-00mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_2-00mm_DF11.kicad_sym
Normal file
5
symbols/Conn_2-00mm_DF11.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_2-00mm_DF3.kicad_sym
Normal file
5
symbols/Conn_2-00mm_DF3.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_2-54mm.kicad_sym
Normal file
5
symbols/Conn_2-54mm.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_DF11.kicad_sym
Normal file
5
symbols/Conn_DF11.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_Misc.kicad_sym
Normal file
5
symbols/Conn_Misc.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_RF.kicad_sym
Normal file
5
symbols/Conn_RF.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Conn_Test.kicad_sym
Normal file
5
symbols/Conn_Test.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Connector.kicad_sym
Normal file
5
symbols/Connector.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Connectors.kicad_sym
Normal file
5
symbols/Connectors.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/DFN.kicad_sym
Normal file
5
symbols/DFN.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/DIP.kicad_sym
Normal file
5
symbols/DIP.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Diode.kicad_sym
Normal file
5
symbols/Diode.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Disc_BJT.kicad_sym
Normal file
5
symbols/Disc_BJT.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Disc_Diode.kicad_sym
Normal file
5
symbols/Disc_Diode.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
5
symbols/Disc_FET.kicad_sym
Normal file
5
symbols/Disc_FET.kicad_sym
Normal file
@@ -0,0 +1,5 @@
|
||||
(kicad_symbol_lib
|
||||
(version 20241209)
|
||||
(generator "kicad_symbol_editor")
|
||||
(generator_version "9.0")
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user