Skip to content

4 The parts database

For whoever writes, checks or looks up a parts entry – by hand, or with the skills part-search and own-part and the agent part-selector. After this chapter you can find a part, write and sign off its entry, keep its datasheet with the project, and enter a part nobody sells. The short way: Settle parts and cost.

In short. Every part has one JSON entry in hardware/parts/: pin types and roles, limits, parameters, SPICE models, leads, and who checked it against which datasheet (the fields, 4.1). The agent part-selector compares candidates at the project's distributors and writes the entry it recommends; the parts library has signed entries to copy (finding parts, 4.2). An entry is signed off against its datasheet, kept with the project, and a critical part gets the second check M-1 (signing off, 4.3; the datasheets, 4.6). A changed entry changes the circuit hash. A part nobody sells – a board of your own – gets an entry derived from its project (own parts, 4.5).

Each part has one JSON file in hardware/parts/, named

  • by its LCSC number: hardware/parts/C17414.json;
  • for a part bought from another distributor, by its manufacturer part number, characters a file name cannot hold replaced by _: hardware/parts/RC0805FR-0710KL.json;
  • for a part nobody sells, such as a board of your own, by the name it was given (own parts, 4.5).

A component finds its entry by lcsc, and without one by part = the entry's mpn. The entry holds what the datasheet says, in a form the checks and the simulation can use: pin types and roles, limits, parameters, SPICE models, lead dimensions, the part's height and its mated plug, and who checked it against which datasheet revision. Examples: the Christmas bell's parts.

4.1 Fields

Field Content
lcsc, mpn, manufacturer identification. lcsc equals the file name, mpn is what a component names in part – STRUCT.identity compares them, with the kind and the value parameters. With a maker that matches by MPN (bom.match = "mpn" in its profile: PCBWay, Eurocircuits, AISLER; [cart]), mpn and manufacturer are required for every assembled part (PARTS.fact/mpn) and go into bom_<board>_mpn.csv, for AISLER into the components of the ODB++ (kicad-cli, 16.5)
order where a part bought separately is ordered, supplier → order code: {"rs": "223-0562", "farnell": "…"}. A value may be {code, moq, step} with the supplier's minimum and pack size, {"lcsc": {"moq": 20, "step": 20}} (the LCSC code defaults to lcsc). The first supplier is the default of edakit order. PARTS.supplier warns about a bom: false part with neither an LCSC number nor order – a C99… library number counts as none, LCSC does not sell it
kind resistor, capacitor, crystal, inductor, fuse, diode, led, led_rgb, npn, pnp, pmos, nmos, njfet, pjfet, switch, dip_switch, button, potentiometer, battery_holder, buzzer, relay, connector, ic, module, motor, solenoid, speaker, heater, optocoupler, esd_array, led_strip, varistor
package, footprint the footprint name must equal the EasyEDA footprint (SYNC.footprint compares it with the PCB)
footprint_data optional: the footprint as data – origin, pads, outline and print in mm –, from which edakit part build builds the project's copy and to which SYNC.footprint holds it; an own footprint as text (4.5)
jlc_class Basic / Extended Part; PARTS.stock warns when JLCPCB's catalogue lists another class
datasheet url, pinout_page; edakit part fetch keeps the PDF beside the project, not in the entry (4.6)
pins pin number → the keys of the pin table below
address a device on a bus: {"base": 104, "pins": {"9": 1}, "also": [112], "on": "6"} – its address with every address pin low, what each pin adds when high (by pin number) – or, for a pin with four states, what it adds tied to each: {"7": {"low": 0, "high": 1, "sda": 2, "scl": 3}} (INA219, INA226: A0 on GND, VS, SDA or SCL) –, further addresses it always answers to (a PCA9685's All-Call), the pin whose net is the bus (default: the pin with the role sda). Two devices on one bus at one address are REQ.forbidden/address (a warning); a pin the graph does not hold high or low leaves the device out
configs, config_default the configurations a part runs in, by name – {"5v": {…}, "3v3": {…}}. Each has about and what differs from the entry: limits and values (a key replaces the entry's, null removes it), pins (per pin number the keys that differ – type, role, thresholds, strap, needs; null removes one), needs and address (replace the entry's, null removes them). A component names its config ("config": "3v3", 3.1); without one config_default applies, without that the entry as it stands. edakit part lint holds every config to the schema as the entry it makes (configs.<name>: …) and every when to the configs; a component's config the entry does not know is STRUCT.entry. In the library: CH340C and CH340G at 5 V or 3.3 V (V3 a 100 nF capacitor at 5 V, on VCC at 3.3 V), the CP2102 with its regulator in use or bypassed, the SSD1306 panel C2890596 in I2C (D0 SCL, D1 and D2 SDA, the BS straps) or 4-wire SPI, the DS18B20 powered or in parasite power, the DS3231 with a backup cell, on VBAT alone or without a battery, the HX711 with or without its regulator, the BME280 in I²C (its default: CSB tied to VDDIO, SDO not floating, the address and the roles sda/scl), 4-wire SPI (SDO a tri-state output) or 3-wire SPI (SDO not connected)
matrix a microcontroller or module whose GPIO matrix routes peripherals to any GPIO: the fn kinds it routes, from its datasheet's GPIO matrix section – ["uart", "i2c", "i2s", "pwm", "can"] on the ESP32. A net's fn of those kinds is then served by any GPIO that can drive it (an input line, rx, also by an input-only pin), the nets of one fn_bus need no common instance, and PWM has no fixed slice (REQ.function). Without it only the pins' functions count, and only those of microcontrollers and modules: a peripheral's own functions do not answer for the MCU
antenna a module with a printed antenna: {"keepout_mm": [x0, y0, x1, y1], "edge_mm": 1} – the area around the antenna that stays free of copper, in mm in the footprint's frame (rotation 0, top), taken from the module's datasheet, and how far inside the board's outer edge that area may lie at most, from the maker's layout guide (Espressif: 1 mm for the ESP32 and ESP32-S3 modules, 0 for the ESP32-C3's). The gate holds the board's copper on both layers to the area where the part sits, and the area to the outer outline – a cut-out inside the board is no edge (GEO.keepout, grade edge, warnings while new); a radio without either is listed by edakit check --coverage
hints warnings of the datasheet for the part as a whole; edakit check shows them and the pin hints of connected pins
hot_loop switching regulators: the pin roles of the loop that carries the pulsed current, from the datasheet's layout section – a buck ["vin", "sw", "gnd"], an asynchronous boost ["sw", "gnd"]; a ground pin without a role is gnd. REQ.missing wants a layout.loops entry on each of their nets and on the net behind a diode on sw
symbol optional: the sides of the box edakit schematic draw draws for an IC or module – {"left": [pins], "right": [pins], "top": [pins], "bottom": [pins]}; without it the pins run as on a DIP package (6.5). With footprint_data and its "symbol": "box" also the box edakit part build draws as the copy's symbol in EasyEDA (4.5)
limits what the part withstands or needs, per kind below – the engine LIMITS holds every quantity to them
values what the simulation and the calculations read, per kind below; behavior for ICs (chapter 5) – matrix_driver for a multiplexing LED driver (MAX7219): the LEDs between its segment and digit pins have no DC state, the simulation lists them as information instead of SIM.state or SIM.driver
spice {"model": ".model SS34 D(IS=20u N=1.2 RS=0.04 …)"} for diodes and transistors
lead through-hole parts only: lead cross-section per pad number or "*": shape (square, round, rect), a_mm, b_mm, tol_mm, hole_rec_mm, source – checks GEO.hole, before the layout on the footprint geometry and in the gate on the drill file; the ring a widened hole leaves: GEO.ring; a footprint with plated holes and no lead keeps GEO.hole pending; optional length_mm, the lead length below the seating plane – the mechanical model gives its protrusion (7.4)
body optional: {"h_mm": 2.3}, the height of the body above the board, on the side the part sits on – for the mechanical model (7.4); box [x0, y0, x1, y1] in footprint mm (relative to the part's origin at 0° on TOP, as mate), the body seen from above where it is larger than the footprint – a coin cell standing out of its holder, from the holder's drawing and the cell's size: GEO.silk warns about print under it beyond the footprint, silk apply places designators beside it (7.7)
mate optional, for a connector: its mated plug with the strain relief, {"box": [x0, y0, x1, y1], "h_mm": 6.5, "exit": "+y"} – box in footprint mm (relative to the part's origin at 0° on TOP, as pcb dump measures it), h_mm its height above the board, exit the direction the cable leaves the box: +x, -x, +y, -y, or +z (away from the board). pcb plan checks it (7.4)
joined optional: pins the part joins inside, as groups – [["4", "29"]], a module's two GND pins. The plan of a perfboard counts each group as one node (PERF.net, 7.9); a button's pins that share a role are joined without it
pad_map only when pad numbers are deliberately swapped on the placed part: {PCB pad: library pad} – check SYNC.footprint
sources where every value comes from; "assumption …" marks what must be checked with extra care – also a value taken from a sibling part or the series. The gate protocol lists every source with the word "assumption" (8.2)
compliance for the regulatory review (M-6, chapter 15): rohs (true, false or the exemption), reach (the supplier's SVHC statement, or "none above 0.1 %" with its source: a board given away or sold names it for every part, else PARTS.fact/reach – information for a gift, a warning for a sale), radio for a part that transmits – bands_mhz ([[from, to], …] in MHz: the bands it can transmit in, as datasheet or certificate state them), max_dbm (the conducted power at the RF port, as datasheets state it – not the radiated power), antenna_dbi (the gain of the antenna it transmits through in the product, from the module's datasheet or certificate), eirp_dbm (optional: the e.i.r.p. where a certificate states it), doc (URL of the EU declaration of conformity); its e.i.r.p. is eirp_dbm, else max_dbm + antenna_dbi, and LIM.eirp holds it to the limit of the band (3.3) –, or "none" for a part that does not transmit: a part of graph type module or microcontroller states one of the two, else PARTS.fact reports it (information, for a board that is sold a warning), and every part the BOM lists (bom not false) states rohs, else PARTS.fact/rohs, graded the same; source (page and date). This is support for the review, not legal advice: nothing in it is legally guaranteed, and it makes no claim to be correct (chapter 15)
verified null or the sign-off: by, date, datasheet (revision), checked (list), open (list: a text, or {"text": "…", "when": "order"} – when names the phase whose manual check answers it: order the assembly preview M-2, acceptance the delivered board M-4), optional datasheet_sha256 (the file signed against, as edakit part fetch recorded it), optional second_check (the independent M-1 review). The gate protocol lists the open points (8.2)
own a part nobody sells (4.5): what – pcb (a board of an edakit project), bin (from your stock, no longer to be had), handmade, copper (the board's own copper – a solder jumper, a test pad, a pad for a wire: no supplier, no compliance facts, no LCSC part or EasyEDA device asked; its pads are the layout's); for pcb its source: project, connectors, graph_version, state_sha256 (the board's circuit hash), and in the copy path, commit, repo, released – SYNC.source compares them

Pin keys

Every pin has name, type, role and v_max_v; the other keys come from the datasheet where they apply.

Key Content
name as in the EasyEDA symbol and identical to the graph – SYNC.schematic compares them
type, role the pin type and the role, below; for polarised parts the role follows the footprint's polarity print when symbol and footprint disagree
strap "high"/"low": level the pin needs at reset (LVL.boot)
pull, pull_ohm "up"/"down": internal or built-in pull resistor, with pull_ohm if known (REQ.missing, LVL.boot)
hint a warning from the datasheet, e.g. "ADC2: no analog reads while WiFi is on"
print on a module's pin: its name as the board prints it, from the pinout page – "D2" where the symbol says PD2. edakit schematic draw writes it beside the symbol's name in brackets, where the two differ, and nothing else reads it
adc on a pin that converts: {"range_v": [0.15, 2.45], "r_source_max_ohm": 10000, "c_hold_min_f": 1e-7} – the window the converter reads in the attenuation the firmware uses, the source resistance it allows, the hold capacitor that lifts that limit. LIM.range/adc holds a net with fn: adc to the window in every scenario, REQ.value/adc the resistors on it, both warnings
z_in_ohm an input's impedance, for the load on an instrument input
short_proof true where a pin may go to a cable without a series resistor – REQ.missing/leave leaves it alone
clamp "none" on an output without clamp diodes of its own – the L298's bridges –, "internal" where it has them: REQ.missing/clamp asks for a diode to the supply and one from ground where such an output drives a winding or a connector
windows on a configuration pin that reads its setting by voltage: [{"state": "right", "min_v": 0.825, "max_v": 1.245}, …], the windows the datasheet guarantees – between one trip point's maximum and the next one's minimum –, with the pin's pull and pull_ohm in the operating point: REQ.value/window
drive "open_drain" or "tristate" on an output that lets go of its line: such outputs share a line without LVL.contention, and a line only they drive needs a pull-up (LVL.undefined/open_drain). "bridge" on a motor driver's half-bridge output, driven high or low while enabled: it defines its net like a push-pull output
state "high", "low" or "off" on an output or bidirectional pin: where behaviour logic holds it in the simulation – the CH340's TXD high when idle; off on an output without a logic level, the MPU-6050's REGOUT. A component's states overrides it (the graph's format, 3.1; the netlist, 5.1)
biased true on an analog input the part holds at its own DC level – an amplifier input at half its supply inside, the PAM8403's INL/INR: a net only a coupling capacitor reaches has its level, no LVL.undefined/analog
float_ok true where the datasheet allows an unused input to float – otherwise an IC input in nc is LVL.undefined/nc. false on a bidirectional pin the part reads and that must not float – the BME280's SDO choosing its address –, which LEVELS then holds like an input
needs what the pin needs from the circuit, from the datasheet's application section – the pin needs below
functions on MCU and module pins: what the pin-mux table offers, <peripheral><instance>.<line> – ["spi0.sck", "uart0.cts", "i2c1.sda", "pwm1.a", "uart0.tx"], adc0; for PWM the instance is the slice and the line the channel. [] = no free function (used on the module), no key = unknown (REQ.function)
v_op_min_v, v_op_max_v from the recommended operating conditions: a supply pin's range where it differs from the part's vdd_op_min_v/vdd_op_max_v
vih_v or vih_ratio, vil_v or vil_ratio from the recommended operating conditions: the thresholds of one input pin, where they differ from the part's (LVL.threshold)
out_edge, t_pd_ns on a serial output (SDO, Q7) of a clocked part: "rising"/"falling", the clock edge after which it changes, and [min, max], the clock-to-output delay at the part's supply, null where the datasheet gives none – edakit check shows them with HINT.readback

Pin needs (needs) are a list of {"what": "capacitor", "c_min_f": 1e-6, "c_max_f": 10e-6, "source": "…"}. what is one of capacitor (to ground), pull_up (to a supply), pull_down (to ground), series_resistor, to_ground, never_to_ground; the three resistors each take r_min_ohm/r_max_ohm, a capacitor c_min_f/c_max_f, either bound optional. Examples: an ESP32's EN with 10 kΩ and 1 µF, a regulator's output capacitor, USB series resistors, MAX6675 T− on ground and MAX31855 T− never. REQ.missing, REQ.value and REQ.forbidden hold them, grade pin, a warning.

A capacitor's c_min_f counts the capacitance left after DC bias (grade bias, the capacitors' dc_bias below). A regulator whose output capacitor's ESR must lie in a window gives esr_min_ohm/esr_max_ohm (TI's LM1117: 0.3 … 22 Ω), held to the capacitors' esr_ohm (grade esr). A need with when holds only in that config – a name of the entry's configs, a list of names – or, with "connected", only while the pin is in a net: an INA226's ALERT wants its pull-up where it is used, a DS18B20's VDD goes to ground in parasite power, a DS3231's VBAT wants a capacitor as the primary supply.

Pin types (type): passive, input, output, bidirectional, power_in, power_output, ground, analog, current_sink, no_connect, mechanical. LVL.undefined, LVL.contention, REQ.missing and PATH.unfed work with them: power_in needs a capacitor and a net that a supply reaches, a ground pin the ground net, two outputs on one net are a conflict, an input needs a driver. An MCU pin that can only read (the ESP32's GPIO34–39) is input, even with the role gpio: the simulation refuses to drive it (LVL.direction).

Pin functions (functions) come from the MCU's pin-mux table, not from memory: the example's Pico 2 entry (tests/fixtures/led-bar/hardware/parts/C41407547.json) has them for the RP2350, from the pico-sdk headers it names in sources – any RP2350 module takes the same list per GPIO. On the RP2040 and RP2350 the PWM slice of GPIO n is (n >> 1) & 7 and the channel a for even, b for odd n.

Pin roles

Pin roles (role) tell the code what a pin does, independent of its name:

Pins by role: anode and cathode of a diode or LED, collector, base and emitter of a bipolar transistor, drain, gate and source of a MOSFET, plus and minus of a polarized capacitor and a battery holder, common and throws of a switch, the ends and wiper of a potentiometer, a and b of a button

kind roles
capacitor the pin numbers 1, 2; a polarized one plus, minus
crystal the pin numbers 1, 2 – a crystal of four pads: the quartz on 1 and 2, the lid pads ground
diode, led anode, cathode
led_rgb common cathode: anode_red, anode_green, anode_blue, cathode; common anode: anode, cathode_red, cathode_green, cathode_blue – a pin with the role anode makes it a common anode
npn, pnp collector, base, emitter
pmos, nmos drain, gate, source; the second channel of a dual part drain_2, gate_2, source_2
njfet, pjfet drain, gate, source
switch common, throw_a, throw_b; a switch of several poles (a 3PDT footswitch) common_2, throw_a_2, throw_b_2, common_3 … – every pole follows the one actuator in the simulation
button a, b (pins connected inside share a role)
dip_switch a1…aN, b1…bN – N is values.positions (default 8): the simulation closes switch 1 … N
potentiometer end_a, wiper, end_b
battery_holder plus, minus (several cells: plus_a, minus_a, …)
buzzer plus, minus
relay coil_plus, coil_minus, common, no, nc; a second contact set (DPDT) common_2, no_2, nc_2, and so on – each set switched by the simulation
motor, solenoid, speaker plus, minus (a motor's and a solenoid's winding coil_ohm, a speaker's impedance_ohm: the simulation's resistor; a switched winding needs its flyback diode, REQ.missing)
heater the pin numbers (value_ohm: its resistance, LIM.p its power)
optocoupler anode, cathode, collector, emitter (vf_v, vf_at_a of the LED; ctr_min_ratio: the transistor passes at least that share of the LED current); a PhotoMOS output load_a, load_b with behavior: "photomos", r_on_ohm, if_on_a (the LED current that guarantees on – the datasheet's maximum operate current), if_off_a (guaranteed off – its minimum turn-off current), i_leak_a, and limits v_load_max_v, i_load_max_a (5.1)
esd_array io1 … io8 (each protected line; two pins of one line share the role), vbus (the steering rail), gnd – limits.vrwm_max_v (the rail's stand-off: LIM.v holds the rail to it without margin), values vbr_min_v, vf_v, vf_at_a, c_io_f; HINT.esd counts the lines on its io pins as guarded
led_strip vdd, gnd, din at the start, vdd_end, gnd_end, dout at the far end (cin, cout: a clocked strip's clock) – the pin numbers are the entry's own; the strip's length is the component's (3.1)
varistor the pin numbers 1, 2 (v_rms_max_v, v_max_v: its VRMS and VDC, held without derating – on an AC net the first, 3.1; designator prefix RV)
connector free (pin1, …); vbus/gnd for a supply input; line, neutral for a mains input (a terminal: PATH.unfed takes the module behind it as fed); cc1, cc2 on a USB-C receptacle (REQ.missing:cc; a module or breakout without these roles: values.usb_c)
ic, module as the behaviour model needs them: vin, vout, gnd, vsys, 3v3, gpio, rext, out, oe_n, vdd; sda, scl for I2C pins (REQ.missing); on shift registers and LED drivers sdi, sdo (serial data in and out) and the outputs numbered out0 … outN where their order matters – the simulation reads them as out, the firmware contract numbers its channels by them (3.5)

Limits and values

Limits and values of a resistor, an LED, an nmos and a capacitor, each beside its symbol with what it is held to – the voltage, current and power at the part

What edakit part lint holds an entry to and the engines read, per kind – generated from edakit/catalog/kinds.toml (python -m edakit.catalog.handbook --write):

kind limits values
every kind p_max_w, rth_ja_k_per_w, tj_max_c, t_max_c, t_min_c –
resistor v_max_v value_ohm, r_max_ohm, tolerance
potentiometer v_max_v value_ohm, tolerance, adjust
capacitor v_max_v, vr_max_v, i_ripple_a value_f, dielectric, polarized, tolerance, dc_bias, esr_ohm
crystal – f_hz, c_load_f
inductor i_max_a, i_sat_a value_h, dcr_ohm
fuse v_max_v, i_max_a, i_hold_a, i_trip_a r_min_ohm, r_max_ohm, i_hold_vs_temp
diode vr_max_v, if_max_a, ifsm_max_a vf_v, vf_at_a, ir_a, ir_at_v
varistor v_rms_max_v, v_max_v v_varistor_v, v_clamp_v
led vr_max_v, if_max_a color, vf_typ_v, vf_min_v, vf_max_v, vf_at_a, rs_ohm, vf_vs_if
led_rgb vr_max_v, if_max_a vf_typ_v, vf_min_v, vf_max_v, vf_at_a, rs_ohm, vf_vs_if
npn vce_max_v, veb_max_v, ic_max_a hfe_min, hfe_max, vce_sat_v, icbo_a
pnp vce_max_v, veb_max_v, ic_max_a hfe_min, hfe_max, vce_sat_v, icbo_a
njfet vds_max_v, vdg_max_v, vgs_max_v, ig_max_a, id_max_a idss_a, vgs_off_v
pjfet vds_max_v, vdg_max_v, vgs_max_v, ig_max_a, id_max_a idss_a, vgs_off_v
nmos vds_max_v, vgs_max_v, id_max_a, is_body_max_a vgs_th_v, rds_on_max_ohm, vsd_v_at_1a
pmos vds_max_v, vgs_max_v, id_max_a, is_body_max_a vgs_th_v, rds_on_max_ohm, vsd_v_at_1a
switch v_max_v, i_max_a, i_contact_max_a, i_switch_max_a r_on_ohm, force_n, min_load_a
dip_switch v_max_v, i_max_a, i_contact_max_a, i_switch_max_a r_on_ohm, min_load_a, positions
button v_max_v, i_max_a, i_contact_max_a, i_switch_max_a r_on_ohm, force_n, min_load_a
battery_holder i_max_a, v_max_v r_contact_ohm
buzzer v_max_v, i_max_a coil_ohm
relay v_coil_max_v, i_max_a, v_max_v, i_contact_max_a, i_switch_max_a coil_ohm, v_operate_v, v_release_v, r_contact_ohm, min_load_a
connector v_max_v, i_max_a, i_contact_max_a, v_rms_max_v pitch_mm, r_contact_ohm, r_source_ohm, usb_c
ic vdd_max_v, vdd_op_min_v, vdd_op_max_v, vin_min_v, vin_max_v, vout_max_v, i_out_max_a, vin_rel_max_v, i_supply_pin_max_a, f_max_hz, r_sense_min_ohm, f_sck_max_hz, f_scl_max_hz, gpio_i_max_a, gpio_i_total_max_a behavior, vout_v, dropout_v, iq_a, efficiency, idd_max_a, iout_gain, iout_accuracy, vout_knee_v, vrext_v, vih_v, vil_v, vih_ratio, vil_ratio, first_bit_to, vout_swing_v, vicm_v, r_out_ohm, comparator, gain_min_ratio, f_clock_hz, vref_v, i_load_min_a, prog_v, icc_a, i_a, i_core_a, i_core_min_a, gpio_r_out_ohm, regulator, vdd_shunt_v, i_shunt_max_a, pd_request_v, r_on_ohm, vgs_th_v, r_on_high_ohm, r_on_low_ohm, v_headroom_v, uvlo_v, v_pre_v, i_pre_ratio, i_term_ratio, v_ovp_v, v_ovp_release_v, v_uvp_v, v_uvp_release_v, v_ocp_v, v_scp_v, vsense_v, duty_max_ratio, ripple_ratio
module vdd_op_min_v, vdd_op_max_v, vin_min_v, vin_max_v, vsys_min_v, vsys_max_v, i_out_max_a, gpio_v_max_v, gpio_i_max_a, gpio_i_total_max_a, f_max_hz, f_sck_max_hz, f_scl_max_hz behavior, regulator, v_io_v, vout_v, dropout_v, efficiency, i_core_a, i_core_min_a, gpio_r_out_ohm, iq_a, vih_v, vil_v, vih_ratio, vil_ratio, usb_c, f_clock_hz
motor v_max_v, i_max_a coil_ohm
solenoid v_max_v, i_max_a coil_ohm
speaker v_max_v impedance_ohm
heater v_max_v value_ohm
optocoupler if_max_a, vr_max_v, vce_max_v, ic_max_a, v_load_max_v, i_load_max_a vf_v, vf_at_a, ctr_min_ratio, behavior, r_on_ohm, if_on_a, if_off_a, i_leak_a
esd_array vrwm_max_v vbr_min_v, vf_v, vf_at_a, c_io_f
led_strip vdd_op_min_v, vdd_op_max_v pixels_per_m, i_pixel_a, i_idle_a, r_vdd_ohm_per_m, r_gnd_ohm_per_m

Per pin: v_max_v, v_op_min_v, v_op_max_v, vih_v, vil_v, vih_ratio, vil_ratio, pull_ohm, v_min_v, v_rel_max_v, v_max_powered_v, i_max_a, i_inject_max_a, r_load_min_ohm, c_load_max_f, pull_v, c_in_f, v_rms_max_v; and name, type, role, strap, pull, hint, functions, out_edge, t_pd_ns, needs, drive, float_ok, adc, z_in_ohm, short_proof, clamp, windows, biased, ref, rel_ref, state, print. Every name ends in its unit: _v V, _a A, _w W, _ohm Ω, _f F, _h H, _c °C, _hz Hz, _dbm dBm, _dbi dBi, _mm mm, _n N, _s s, _ns ns, _k_per_w K/W, _ratio 1, _ohm_per_m Ω/m, _per_m 1/m. Words: behavior ldo, buck, boost, buck_boost, mcu, cc_sink, opamp, logic, current, bridge, charger, protection, pd_sink, pass_gate, photomos, matrix_driver, cc_led_driver, none; regulator ldo, buck, boost, buck_boost, none; color red, green, blue, yellow, orange, white, warm_white, amber, uv, ir, rgb; dielectric C0G, NP0, X5R, X7R, X7S, X6S, Y5V, electrolytic, polymer, tantalum, film; adjust panel, trimmer.

A limit named <quantity>_max_<unit> or <quantity>_min_<unit> is held to the quantity of that name at the part (LIM.v, LIM.i, LIM.p, LIM.t, LIM.range – the catalog, chapter 14.2). A limit on a pin (v_max_v) comes before the entry's, the entry's before the default of the pin type. The sections below give what the names do not say.

Supplies, pins and heat

  • MCU modules: gpio_i_max_a per output pin and gpio_i_total_max_a for all outputs together, sourced and sunk separately – the package's supply pins. With an on-board LDO (regulator: "ldo"), that regulator's rth_ja_k_per_w and tj_max_c from its datasheet: LIM.t holds its junction temperature with the core current it carries (5.3).
  • any IC or module vdd_op_min_v, vdd_op_max_v: the operating supply range of the recommended operating conditions, not the absolute maximum – LIM.range holds every supply pin to it. A supply pin with another range has its own v_op_min_v / v_op_max_v. Own boards (behaviour current) give vin_min_v, vin_max_v – the voltage at the connector over the board's own simulation (own parts, 4.5).
  • any part that dissipates – resistor, diode, LED, BJT, MOSFET, JFET – rth_ja_k_per_w (junction to ambient) and tj_max_c: LIM.t holds ambient + P × Rth(j-a) to the junction limit and, on a board people touch, to touch_c (5.3); without rth_ja_k_per_w the part gets no temperature item.
  • a constant-current driver: limits.i_supply_pin_max_a, what its ground pin carries – LIM.i/supply_pin holds all its outputs together.
  • an MCU: values.f_clock_hz and limits.f_max_hz as a table by supply voltage ({"2.7": 10e6, "4.5": 20e6}) – LIM.range/clock holds the clock to what its supply allows.
  • a part on an SPI or I2C bus: values.spi_modes (the modes 0–3 its interface runs in, CPOL × 2 + CPHA, from the timing diagram), limits.f_sck_max_hz and limits.f_scl_max_hz (its highest SCK and SCL – for I2C the mode it runs without a high-speed master code), and on its SDA and SCL pins c_in_f, their input capacitance. The firmware contract carries them (3.5, devices, bus_speed); LIM.range/clock holds a net's clock_hz to them and to what the pull-ups allow with the pins' capacitance.
  • an IC whose pins must not rise above its supply: limits.vin_rel_max_v, how far above (e.g. 0.5 V) – LIM.v/inject holds its inputs, bidirectional pins and outputs in every scenario.
  • a pin of such an IC with a limit of its own gives v_rel_max_v against the pin its ref names ("ref": "VDDIO", the BME280's interface pins), else against the part's supply; null for a pin rated above its supply (TP4056 CE). Where only a resistor feeds that pin, i_inject_max_a: the current the clamp takes, (V − V_ref − v_rel) / R, is then held to it instead of the voltage. vih_ratio and vil_ratio refer to the same ref; where the datasheet refers the maximum to another supply than the thresholds, rel_ref names that one (the MPU-6050's AD0: thresholds of VLOGIC, at most VDD + 0.5 V).
  • pin limits beyond the voltage: v_min_v – the pin's own lower limit (an RS-485 line, −7.5 V) in place of the −0.3 V floor of an active pin. v_max_powered_v beside v_max_v where a pin takes more only while the part is powered (the RP2350's fault-tolerant GPIOs: 5.5 V powered, v_max_v unpowered) – stage G warns with grade powered, each scenario of stage E holds the one that applies.
  • pin currents and loads: i_max_a, the pin's own current, and i_group_max_a of the entry, {group: {pins = [numbers], max = A, dir = "sourced" | "sunk"}} for a sum (an AVR's ports, whose datasheet sums sourced and sunk currents over different pins; an MCU's sums from its output ammeters) – LIM.i/pin on the operating point where resistors, LEDs and diodes alone are on the pin's net. r_load_min_ohm and c_load_max_f, what an output may drive (ACS712 VIOUT: 4.7 kΩ, 10 nF) – REQ.value/load on the DC load and the capacitors of its net.
  • two supplies in a fixed order: supply_order, [[lower pin, higher pin]] by pin name (the MPU-6050's VLOGIC ≤ VDD) – LIM.v/order on the declared ranges and in every scenario.
  • a pin's own pull-up to an internal supply: pull up, pull_ohm and pull_v, the voltage it pulls to (the PT4115's DIM: 200 kΩ to 5 V) – the constant-current LED buck's model puts it into the operating point.
  • any IC with logic inputs: the thresholds of the recommended operating conditions, vih_v | vih_ratio and vil_v | vil_ratio (a ratio of its supply, e.g. 0.7 and 0.3 for VIH 0.7 × VDD), which LVL.threshold holds its input pins and the MCU outputs that drive them to; more on ICs in the simulation (chapter 5).

Passive parts and contacts

  • resistor value_ohm 0 is a bridge that joins its nets (3.3); r_max_ohm its maximum resistance from the datasheet, simulated instead of 0 Ω (50 mΩ without it).
  • LED vf_typ_v, vf_min_v, vf_max_v, vf_at_a, rs_ohm – an RGB LED one set per colour under red, green, blue, limits too. vf_vs_if, [[A, V], …]: points of the datasheet's typical IV curve, at least two, currents and volts rising – the simulation's ideality follows them (5.1), 2 without.
  • switch, connector, battery holder i_max_a: what the part carries as a whole – a USB-C receptacle's two ground contacts at 1.25 A each are 2.5 A. Fuse i_hold_a, i_trip_a with values.i_hold_vs_temp ([[°C, A], …]), which LIM.i reads at the lowest ambient of sim.json.
  • contacts – switch, button, DIP switch, relay, connector: i_contact_max_a, what a closed contact carries (a connector's current rating per circuit, the JST XH's 3 A at AWG 22); i_switch_max_a, what it may break when it opens (a switching or electrical-life rating: the DSHP DIP switch's 25 mA at 24 V DC); i_max_a where a datasheet does not say which. The simulation holds the current through every closed contact to both (LIM.i; the switching rating grade switching, a warning), a supply's current at a connector shared among the connector's pins on the supply's net, and a fuse's hold current to i_contact_max_a as to i_max_a.
  • values.min_load_a, the least current a contact needs to close reliably (a relay's "minimum applicable load", the HFD4's 10 µA): the DC through a closed contact at least that, and a contact in a signal path (3.1, net signal) rated for signal currents – min_load_a at most 10 µA (REQ.value/min_load, a warning).
  • buzzer coil_ohm: a magnetic buzzer, a coil for REQ.missing (flyback diode) and the simulation, which holds its coil current to i_max_a (5.1); relay v_operate_v, v_release_v (must-operate and must-release voltage).
  • a polarized capacitor (an electrolytic, a tantalum): values.polarized: true and the pin roles plus and minus; vr_max_v the reverse voltage the series allows – LIM.v holds it in the declared ranges and the simulation, and PATH.reverse reports one a reversed source reaches (both grade polarized, warnings).
  • an AC rating – a mains terminal's 300 V AC, a mains module's input range 85 … 265 V AC, a varistor's VRMS: v_rms_max_v in V rms, of the entry (connector, varistor) or of a pin. On an AC net (3.1, net ac) LIM.v holds the rms of what the part sees to it, and not its DC rating (v_max_v, a pin's v_min_v); elsewhere the DC rating holds. A rating the datasheet gives only for AC goes there, not into v_max_v.
  • a potentiometer set once at the bench and not turned while the board runs – a bias trimmer, a multiturn 3296: values.adjust: "trimmer" ("panel", the default, a pot on the front panel). REQ.value/switch holds a panel pot in a signal path to one DC level on both sides – the scratch of a control turned while playing –, a trimmer not.
  • a TVS diode: values.tvs: true, its working voltage (VRWM) as vr_max_v – LIM.v holds the reverse voltage to it without the derating factor; a bidirectional one (the CA suffix) values.tvs: "bidirectional": LIM.v holds it either way, and PATH.reverse takes it as a part that tolerates a reversed source.
  • a class-2 ceramic (X5R, X7R …): values.dc_bias, the share of its capacitance left at a DC across it as [[V, ratio], …] – the maker's curve for this part, volts from 0 to the rated voltage (KEMET's 4.7 µF 6.3 V 0603 X5R, C5126849: [[0, 1.0], [1, 0.97], [2, 0.89], [3, 0.72], [4, 0.54], [5, 0.43], [6.2, 0.35]]). Makers give it as a graph or an interactive tool. The points are read off the graph of the part's own sheet, and the source says "read from the graph"; KEMET's and YAGEO's part sheets (search.kemet.com) carry it, Samsung's, Murata's and TDK's could not be read for the library.
  • edakit interpolates dc_bias linearly and takes the last point beyond the curve; the capacitance left at the highest DC the declared ranges of the capacitor's two nets put across it counts where a minimum is judged – a pin need's c_min_f (REQ.value/bias where only the bias makes it fail), an ADC's c_hold_min_f – and in the transient and AC netlists (5.1); a maximum (c_max_f, c_load_max_f, the USB attach limit) stays on the nominal value. A capacitor without a curve counts with its nominal value – a choice for no false findings: a missing curve says nothing about how much is lost –, and edakit check --coverage names every class-2 capacitor a minimum counted that way.
  • values.esr_ohm: the ESR at 100 kHz, one number or [min, typ, max] with null where the maker gives none – a tantalum's maximum from its series table (KEMET T491A106K016AT: 3 Ω), a ceramic's typical from the maker's graph (about 0.01–0.2 Ω). Against a need's esr_min_ohm the lowest known value counts, against esr_max_ohm the highest. The ESR window holds when the capacitors inside it reach the need's minimum after DC bias; one without esr_ohm leaves it unjudged, and the coverage report names it.
  • an addressable LED strip (led_strip, 5.1): pixels_per_m; i_pixel_a, one pixel at full white (every channel at full brightness), i_idle_a with all of them dark (default 0); r_vdd_ohm_per_m and r_gnd_ohm_per_m, the copper of the +V and the GND rail per metre of strip – no LED data sheet gives them: the strip's seller, a measurement, or the project's own value with its source. Without any of these four PARTS.fact names it (a warning) and the strip's scenarios stop instead of guessing.
  • the strip's limits vdd_op_min_v, vdd_op_max_v are the pixel IC's supply range – LIM.range/strip holds every pixel along the strip to it; a pin's i_max_a, the rating of the strip's lead or connector there, the feed at that pin (LIM.i/strip). In the library: Pololu's WS2812B strip at 60 LEDs/m (Pololu_2549), its rail resistance derived from Pololu's published measurement.

Transistors and ICs

  • a MOSFET used as a switch: vgs_th_v ([min, typ, max]) and rds_on_max_ohm as a table {"-2.5": 0.085, "-4.5": 0.06} (VGS → Ω) – LIM.range/gate reports one whose |VGS| lies between its threshold and the lowest VGS its RDS(on) is specified at: a P-MOSFET on 5 V that a 3.3 V output cannot switch off, an IRF520 at 3.3 V.
  • the spread of a transistor, the corners of the simulation's spread (scenarios, 5.2; maker-coverage FT-17): a BJT's hfe_min, hfe_max – the DC gain's minimum and maximum from the datasheet's table, at the collector currents its stages run at (the 2N3904: 70 at 1 mA, 300 at 10 mA) – and icbo_a, the collector-base leakage by temperature, {"°C": A}. That is the datasheet's maximum, at more temperatures where it gives them, or, as the 2N3904's, the maximum at 25 °C rising as the typical curve does – say so in sources. A MOSFET's spread is vgs_th_v [min, typ, max].
  • a JFET's spread (kind njfet, pjfet): idss_a and vgs_off_v as [min, typ, max], VGS(off) negative for an n-channel part, null where the datasheet gives no typical value; its limits vds_max_v, vdg_max_v, vgs_max_v (the gate's reverse voltage against the source), ig_max_a (the forward gate current), id_max_a.
  • the model in spice is the typical part (.model MMBFJ201 NJF(VTO=-0.77 BETA=0.98m LAMBDA=0.016)); the corners set BF, VTO, KP and BETA from the spread fields. In the library: 2N3904 (C124482), MMBFJ201 (C891687, the J201 in SOT-23), BS170 (C111691), 2N7000 (C232838), the pedals' 2SC1815 (C23895, JSCJ's rank GR, hFE 200 … 400) and 2N5458 (onsemi, IDSS 2 … 9 mA, VGS(off) −1 … −7 V), and the spreads the other transistors' entries already gave. A germanium part (AC128) has no datasheet edakit could read: a project enters its own measured spread, with its source.
  • an op-amp that is stable only above a gain: values.gain_min_ratio – REQ.value/gain against its closed-loop gain. One that needs an external compensation capacitor gets it as a pin need to another pin ({"what": "capacitor", "to": "COMP/BAL", "c_min_f": 22e-12}) – where it needs it only below a gain, in a config with the lower gain_min_ratio (when). The NE5534 is stable from 3, in its config compensated from 1 with CC ≥ 22 pF, and REQ.value/gain names that config in its fix; the LM301A (C22505) always needs its 30 pF from COMPENSATION to BALANCE/COMPENSATION. In the library beside them the TL072CP (C7236), unity-gain stable.
  • shift registers and LED drivers first_bit_to: the output the first shifted bit ends on, e.g. "out7", from the datasheet's timing diagram – copied into the firmware contract (3.5).
  • a module or breakout with a USB-C receptacle usb_c: {"cc": "rd_5k1" | "controller" | "none", "source": "…"} – how the board terminates its CC lines, from its schematic (REQ.missing:cc; the RP2350-Zero has 5.1 kΩ on CC1 and CC2, the breakout Soldered 333011 has none).
  • a linear Li-ion charger: prog_v, the constant of its charge-current equation (I = prog_v / R_PROG – MCP73831 1000 V, TP4056 1100 V or 1200 V by maker, read from the datasheet of that maker), its pins prog and bat, and i_out_max_a. LIM.i/charge holds the current the PROG resistor programs to the charger and to the cell's i_charge_max_a – of the cell state a battery holder on its battery pin has in any supply scenario of sim.json.
  • a charger with behavior: "charger" – the simulation charges (5.1): vout_v the float voltage, i_pre_ratio and v_pre_v the precondition current (a ratio of the programmed one) below its threshold, i_term_ratio the termination current, r_on_ohm the pass element, v_headroom_v the least vin − bat it charges at, uvlo_v its start voltage, iq_a its supply current while charging.
  • a bridge driver (behavior: "bridge", DRV8833, TB6612): r_on_high_ohm and r_on_low_ohm, the on resistance of its high and low side (a datasheet that gives only their sum – the TB6612's Vsat(U+L) – split evenly, and say so in verified), and the pin roles in1_x, in2_x, out1_x, out2_x, pwm_x, sense_x per channel x, vm, en, vcc.
  • a cell protection IC (behavior: "protection", DW01A): v_ovp_v, v_ovp_release_v, v_uvp_v, v_uvp_release_v (overcharge and overdischarge detection and release), v_ocp_v and v_scp_v (overcurrent and short at VM), r_out_ohm (the outputs' resistance), and the pin roles vdd, gnd, od, oc, vm; its dual MOSFET the roles gate_2, source_2 (and drain_2) of the second channel.
  • a sensor, a transceiver, a USB-UART, an EEPROM – any digital IC that is no regulator, driver or MCU (behavior: "logic"): icc_a, its supply current – the maximum of the datasheet's operating current, the typical value where it gives none (say so in verified) –, drawn at the supply pin (the power_in pins with the role vdd or vcc, else every power_in pin), or a table by pin name where the datasheet gives a current per supply ({"VDD": 0.0039, "VLOGIC": 0.0001}, the MPU-6050); a config may give its own (the CH340 12 mA at 3.3 V, 20 mA at 5 V).
  • a logic IC's outputs rest in the operating point as their pin's state says – high (to its supply, the pin its ref names, else the supply pin), low or off –, without one an open-drain output off, a tri-state output off unless the part ties every enable pin active, a push-pull output low. state comes from the datasheet where it names the idle level (the CH340's TXD high when idle, the HX711's DOUT high while no data is ready); off also on an output that has no logic level, a regulator's or charge pump's capacitor pin, an oscillator output (5.1).
  • a module's on-board regulator values.regulator: ldo, buck, boost, buck_boost (the Pico's RT6150) or none.
  • an adjustable regulator: vref_v and the pin role adj or fb, with i_load_min_a where it needs a minimum load (chapter 5.1). An asynchronous boost with an external rectifier and no output pin (the MT3608) gives sw and en instead of vout: its output is the net behind the diode on sw.
  • a constant-current LED buck (behavior: "cc_led_driver", PT4115, AL8860): vsense_v, the mean current sense threshold [min, typ, max] (I = vsense / R_S), r_on_ohm the switch, duty_max_ratio its maximum duty cycle where the datasheet gives one, ripple_ratio the inductor's peak-to-peak ripple over the mean current, uvlo_v, and limits.r_sense_min_ohm where the datasheet names the least sense resistor; the pin roles vin, sense (CSN, SET), sw, gnd, dim (DIM, CTRL) – chapter 5.1.

The parts library

The parts library (edakit/parts/) holds entries of popular parts and of the parts the building blocks use – resistors, LEDs, the SS14/SS34 diodes, S8050, AO3401A, a USB-C receptacle, a polyfuse, buttons, a slide switch (C22355741), a CR2032 holder and a 9 V battery snap (C70374), JST connectors. Each is written from its datasheet (the source with its revision) and counts once it is signed in edakit: edakit part sign <key> --by NAME writes the signature with the entry's SHA-256 into edakit/parts/signed.toml, and an entry changed after its signature is a draft again (rules-at-scale.md, decision 8).

edakit part add lists the entries, one line each – key, kind, part number, signed or draft –, so edakit part add 2>&1 | grep switch shows the switches; edakit part add <key> copies one into the project's parts folder – edakit block add does the same for a block's parts. A part the library lacks gets its entry written: edakit part import <LCSC> reads its pins and footprint, the datasheet the rest – kind, roles, limits, values, sources –, by the skill part-search or by hand (4.2, 4.3); a part nobody sells, from your notes (4.5). The copy names its library version in library (key, sha256 of the entry, signed, date) and belongs to the project from then on: checked like every entry, signed in the project like every entry. PARTS.library notes a copy whose library entry has changed since (grade newer) – edakit part add <key> --update takes the new version over the copy on purpose and names what changed – and a copy of an unsigned entry (grade draft).

Sources of the audio band are no entries. A guitar's pickup, a piezo disc on an instrument, an electret capsule on a jack drive an AC run from outside the graph or beside it. Their numbers are rows of edakit/catalog/tables/audio_sources.toml that sim.json's ac names ("source": {"pickup": "Seymour Duncan SH-1 '59 Model neck", "l_h": …} – scenarios, 5.2). A new row is a table row like every other – source and tag = "r", catalog lint holds it. A capsule's load resistor and coupling capacitor are parts of the graph; the capsule itself has no kind of its own in the parts schema, so its supply current is not in the operating point.

Each row holds only what its maker states, read from the maker's page or data sheet with the date: a pickup's DC resistance (Seymour Duncan, DiMarzio, Mojotone) and, where given, its resonant peak; a piezo disc's capacitance at 1 kHz with its tolerance, resonance and resonant impedance (Murata 7BB, via LCSC); a capsule's output impedance, the load resistor of its measurement circuit, sensitivity, operating voltage and current (CUI, INGHAi, via LCSC). No maker reachable states a pickup's inductance: the project gives its own, measured or published (decision 20).

4.2 Finding parts

The agent part-selector searches at the distributors of [project] distributors (default ["lcsc"]) in its own context – datasheets and distributor pages do not fill the design session. It compares the candidates and writes the entry of the one it recommends. How to search at each distributor is in its profile, skills/part-search/distributors/<name>.md; a new distributor is a new profile, not a new agent.

Which distributor fits depends on how the board is built. JLCPCB assembles from LCSC's stock, so a part from RS or Farnell is hand-soldered (bom: false), goes to another assembler, or reaches JLCPCB through its Global Sourcing or as a consigned part – weeks and fees, your decision. In EasyEDA it needs the footprint of a part in the same package (easyeda_device).

State: what an agent can reach, tried in October 2026.

Distributor Without a key With a key in the environment
LCSC / JLCPCB edakit part search (JLCPCB's catalogue), EasyEDA's device search, the part pages (curl) – (APIs only on application)
Reichelt search and part pages; stock without a count – (no API)
DigiKey WebFetch: search by part number leads to the part page; no RoHS/REACH DIGIKEY_CLIENT_ID, DIGIKEY_CLIENT_SECRET
Mouser nothing (the denial comes with HTTP 200) MOUSER_API_KEY
Farnell nothing (403) ELEMENT14_API_KEY
RS nothing (403); a web search finds the order number only from RS's account managers

Keys belong in the environment of the session, never into the repository. Without a key the agent says what it could not see – stock from a search snippet is no stock figure.

At LCSC and JLCPCB

  • edakit part search "RGB LED 5mm common cathode" asks JLCPCB's public parts catalogue – the endpoint of PARTS.stock, no login, no EasyEDA, no bridge, no claim. One line per hit: LCSC number, manufacturer part number, manufacturer, class (Basic, Extended, preferred), JLCPCB's assembly stock, package, category; Basic first, then by stock. --basic keeps Basic and preferred parts, --min-stock N drops the rest, --limit N (25) is the page asked of the catalogue, --json FILE writes the hits. Exit 2 when nothing is found, 1 when the catalogue does not answer.
  • An LCSC number gives only its exact hit (the search is full text: C2297 also finds MIC2297). Function and package words work here, unlike in EasyEDA's search: that one query lists the Kingbright C6751358 and the XINGLIGHT C7371894.
  • "Not available" only after three routes, each named with its queries: this search, EasyEDA's device search, the manufacturer's series name (the LCSC profile).
  • edakit part import C2297 reads exactly that number from EasyEDA's public component data – the endpoint easyeda2kicad reads, no key, no EasyEDA, no bridge: manufacturer, manufacturer part number, footprint name, Basic/Extended and the symbol's pin numbers and names. Without an entry it writes a new one with these and verified: null – kind, pin types and roles, values and ratings are for whoever signs it off (4.3). An existing entry is never overwritten: the command lists where it differs from the library (exit 2), --update fills in only the fields it lacks.
  • Either way part import puts the symbol into <artifacts>/library/<LCSC>.json and the footprint into <artifacts>/footprints/<footprint>.json, in the units and axes of pcb dump (mil, y up, rotation 0 on TOP), each pad with shape, size and, for a hole, hole [w, l] in mm and plated. With the symbol cached, STRUCT.entry compares the graph with it (3.3); pcb plan takes the footprint for a part that is not in the PCB yet (7.4).
  • The import's endpoint is undocumented: an unknown number answers HTTP 200 with success: false ("not in the library", exit 1), and after some two dozen requests in a few minutes it answered HTTP 403 for a while – import what you need, not the catalogue.
  • easyeda_schematic_search_device with the LCSC number or the manufacturer part number. It returns footprint, manufacturer number, Basic/Extended and the parameter table (otherProperty), but no stock and no price. Free text ("led 5mm red") returns mostly noise; the classification filter has no effect. Series names work (e.g. XL-304, KT-0805, UNI-ROYAL 0805W8F…).
  • Exact match only: the search is full text – take hits whose supplierId equals the number.
  • C99… numbers are user libraries without an orderable part. EasyEDA's library returns them like any part, but nobody sells them: part search drops them, part import refuses them, and STRUCT.identity refuses one on a part that goes into the BOM.
  • Stock and price: https://wmsc.lcsc.com/ftps/wm/product/detail?productCode=Cxxxx (stock, price breaks, parameters). LCSC's own search blocks automated requests (HTTP 403); jlcsearch is too thin for single parts.
  • JLCPCB's assembly stock differs from LCSC's. Where JLCPCB is among [cart] makers, edakit stock asks JLCPCB's public catalogue (selectSmtComponentList) and part page (jlcpcb.com/partdetail/<C…>) about every assembled part and writes a dated snapshot, hardware/stock.json: listed, Basic/Extended/preferred, stock, MPN, PCBA type ("Economic and Standard" or "Standard Only"), MSL, X-ray, machine or hand soldering, attrition (lossNumber), minimum (leastPatchNumber) and price breaks. edakit stock --lcsc C… prints the same for candidates before they are in the graph.
  • Check PARTS.stock reads the snapshot on every edakit check, offline. A part not listed (pre-order only), short of stock for quantity boards – under Standard PCBA plus the attrition, max(n + attrition, minimum) –, under another MPN than its entry, or "Standard Only" on an Economic order ([release] assembly.pcba) fails for JLCPCB as the planned maker and warns as another one. MSL ≥ 5 (baked for a fee and two days), stock within stock_margin_pct and another class than jlc_class warn; a snapshot older than stock_max_age_days is pending. At the export PARTS.stock asks the catalogue again.
  • Without JLCPCB among the makers edakit stock stops (exit 1), PARTS.stock is a note, and ORDER.parts holds the maker's own matching to the release at the cart (ordering, 8.4).
  • Report stock as a number with a date. "Few" is not information.

4.3 Signing off a part

  1. edakit part fetch <key> and read <datasheets>/<key>.pdf (4.6); note its revision, and its sha256 from <key>.json as verified.datasheet_sha256.
  2. Check pinout and pin names, polarity at the footprint (which pad is pin 1, anode, +), package, every value in values and limits, especially the ones marked as assumption.
  3. Correct the file, extend sources.
  4. verified: {"by", "date", "datasheet", "checked": [...], "open": [...]}.
  5. Critical parts (MCU, regulators, drivers, polarised parts, connectors) get an independent second check (M-1) by the agent electrical-reviewer in its own session, recorded as second_check.

Every change to a parts file changes the circuit hash: sign-offs with scope circuit must be renewed (chapter 8). With [release] hash_ignore (hashes and sign-offs, 8.2) the keys listed there are left out – sources, notes, verified and datasheet are the usual ones –, so recording a review or correcting a source voids nothing; a pin, a rating or a parameter still does.

4.4 Library footprints are not always right

A real project had a 5 mm LED whose EasyEDA footprint printed "+" next to pad 1, while symbol and datasheet called pad 1 the cathode. Editing the project's footprint through the API went nowhere (chapter 9.4). The fix is in the graph: the net of the anode goes to the pad with the "+" print, and the parts entry gives pin 1 the role anode – so every check, the simulation and the firmware pin tables follow.

A library footprint that prints + at pad 1 while the datasheet calls pad 1 the cathode, and the fix in the graph: pin 1 gets the role anode and the anode's net, so print and roles agree

The gate's polarity sheet – one tile per polarised part with copper, silkscreen, pad numbers, roles and nets – makes this visible at a glance. GEO.mark of the gate finds it on its own: it reads the "+" the footprint draws in the silk Gerber and fails when the nearest pad with a polarity role is a cathode (the gate's stages, 8.1).

Library symbols are not always right either – and a schematic is drawn from the symbol, whatever the parts entry says. Writing the parts library (4.1) from the datasheets turned up six whose EasyEDA symbol differs from the manufacturer's datasheet, written up for EasyEDA and LCSC in upstream/issue-6-easyeda-symbols.md:

  • the WS2812B-V5/W (C2874885) numbers DIN 3 like VSS, so a schematic puts the data input on ground and leaves pad 4 open;
  • the XC6206P332MR-G (C5446) names pin 2 Vin and pin 3 Vout, Torex the other way round – the regulator fed backwards;
  • the DRV8825PWPR (C81582) numbers its exposed pad 28 like GND and has no pin for pad 29, the PowerPAD;
  • the Raspberry Pi Pico 2 (C41407547) numbers the test pads TP2–TP6 like pins 2–6 and swaps SWCLK and SWDIO;
  • the 6N138S-TA1-L (C22451885) names the LED's anode "2" and its cathode "NC";
  • the AQY212GS (C719745) has three pins for four pads.

edakit part import caches the symbol with the numbers it gives twice, and STRUCT.entry compares it: names and numbers with the graph's pins as before, and – grade symbol, an error – a number given to two pins, a pad of the footprint without a pin, a pin without a pad.

Where the parts entry follows the datasheet on purpose, symbol_differs names each such pin with the reason read from the datasheet: {"4": "DIN is pin 4 – Worldsemi WS2812B-V5/W V6.1, pin table"}. The pin is left out of the comparison, and a note (grade declared) tells the project that a schematic drawn from that symbol wires it otherwise – check that schematic against the datasheet pin by pin, or take another part number whose symbol is right.

4.5 Own parts

Some parts nobody sells: a board of your own plugged onto the main board, a part from the bin that is no longer made, something made by hand. Nobody wrote a datasheet for them. For a board designed with edakit, its project is the datasheet – edakit derives the parts entry from it:

  1. In the board's project: edakit part datasheet --connector J1 – several connectors: repeat --connector, their pins are numbered on in that order. It writes hardware/datasheet/<name>.json, the parts entry, and <name>.md, the datasheet to read; the name is the project's unless --name gives another. Derived from the board:
  2. pins: the net on the board is the pin's name. The simulation's source is power_in / vin, the ground ground / gnd, a rail the board makes power_output / vout, an I2C line bidirectional / sda or scl; any other pin takes the strongest type among the parts on its net (output, bidirectional, current_sink, input, analog, else passive). A resistor to a supply or to ground gives pull and pull_ohm (REQ.missing, LVL.boot count them); v_max_v is the lowest limit on the net – a pin's v_max_v, a capacitor's v_max_v / 1.5
  3. values: behaviour current, the largest current from the connector over the board's scenarios (its DC scenarios: fault cases with info and transient runs are left out)
  4. limits: vin_min_v, vin_max_v – the voltage at the connector over those scenarios
  5. compliance: RoHS, REACH and radio as the board's parts entries state them – radio lists their radios, is "none" when none transmits and every module and microcontroller of the board says "none", and is left out while one of them states nothing (PARTS.fact in the project that uses the board)

The board's simulation must pass, and exactly one of the connectors must be a source of it: the supply the board draws from the project that uses it. edakit part datasheet without arguments renews every datasheet of the project. Commit them – a datasheet belongs to a state of the board, and SYNC.source says when it is older. 2. In the project that uses the board: edakit part own ../board copies the datasheet into the parts database and adds where it comes from: the path, the commit, the repository, and the gate run that released this state of the board if there is one and it was neither superseded nor withdrawn (<reports>/*/fab/manifest.json of the board's project, 8.3). The component names it like a part bought elsewhere – "part": "<name>", "bom": false (JLCPCB does not have it), the pins as in the entry – and gets an easyeda_device: a symbol with these pin names and the board's footprint, from your EasyEDA library. 3. Sign it off like any part (4.3), against the datasheet at that commit. Copying the same state again keeps the sign-off; a changed board needs a new one.

The checks then treat the board like any IC: its pins take part in LIM.v, LVL and REQ, the simulation draws its current from the supply pin, and LIM.range holds the voltage there inside vin_min_v … vin_max_v – the board was simulated only in that range; give its own sim.json more if the host needs more. SYNC.source compares the copy with its source:

Finding Result
the board's project changed after its datasheet pending – renew it there, copy again
copied from uncommitted changes pending – commit there, copy again
the board is not released at the state of its datasheet warning – release it there and copy again, or accept the warning in the sign-off file (8.2)
the board's project is not there (another machine, CI) note – the copy is not compared
a datasheet of this project is older than the project pending – edakit part datasheet

Not covered: the board's logic levels and timing – its signal pins are typed, not simulated. A board on a cable is no part of the host's PCB: it goes into the same project as a second board (cables, 3.1), or the host's connector gets its current as an external load (5.1).

Bought and hand-wired boards

Bought boards – an MCU module, a sensor board, a USB-C breakout – are parts with an entry like any other (kind module, or connector for a breakout that only passes lines on), written from the seller's schematic, with "bom": false when JLCPCB does not assemble them. Plugged or soldered onto the board (pin headers, castellated pads) they are components of that board: the footprint is the header or pad row, the pins are the ones the board brings out. REQ.missing:cc checks a USB-C receptacle on such a board in one of two ways:

  • its CC eyes as pins of the entry – a connector with the roles cc1, cc2, or a module whose entry says values.usb_c {"cc": "none"}: REQ.missing:cc wants the two 5.1 kΩ on those pins, as parts of the graph;
  • its receptacle inside the board, CC lines no pins of the entry (an MCU module with USB-C on board): values.usb_c says what the board has – rd_5k1 and controller pass, none fails.

A bought board on a cable – a breakout in an enclosure wired to the board's power connector – is a board of its own that nobody makes:

[boards.usb]                 # Soldered 333011 on the cable W1 to J1 – bought, nobody draws it
title = "USB-C breakout"
bought = true                # no template, schematic, pcb or file: there is nothing to draw (10.1)

Its parts are components with "board": "usb" and "bom": false – the breakout with the eyes it brings out, and what you solder onto it, such as the two 5.1 kΩ from CC1 and CC2 to GND; STRUCT wants bom: false on every one, nobody assembles them. A cable joins it to your connector (cables, 3.1) with the resistance of its wires. The checks and the simulation see it like any board: REQ.missing:cc the CC eyes, LIM.i the cable's rating (its i_max_a, from the wire's own datasheet), and sim.json's source sits at the breakout's connector, with the cable in the path (5.1).

Nobody draws it: the gate's stages S and L/F leave it out with an information row each, the manifest, the fab hash, the layout hash and so the layout review M-5 cover the made boards (8.1, 8.2), and the commands that make or read a layout refuse it with one line (--board usb) or skip it. edakit order writes no BOM for it and puts its parts into the carts and under its board, marked bought, in hand_assembly.md; edakit cost prices no PCB and no assembly for it (8.4).

A hand-wired board – parts with no PCB of their own, wired by hand: a front panel's LEDs, footswitches, jacks and pots, a strip on a cable – has a key of its own:

[boards.panel]               # LEDs, footswitches and jacks in the lid, soldered to the wires of W2
title = "Front panel"
wired = true                 # hand-wired, no PCB of its own: no template, schematic, pcb or file (10.1)

edakit treats it as a bought board where that applies: in the graph, the checks and the simulation like any board – its parts with "board": "panel" and "bom": false (STRUCT.board), the cables with their wires' resistance and rating –, made by nobody: stages S and L/F, the manifest, the hashes, the layout commands, the assembler's BOM and the PCB cost leave it out. What differs is what it is.

A bought board is one purchased assembly – a module with its own parts entry and supplier, perhaps with a few parts soldered onto it. A hand-wired board's parts are the project's own: each in the carts from its supplier (edakit order, PARTS.supplier for one without), listed under its board in hand_assembly.md as "hand-wired, no PCB". bought = true on a front panel works as it did; STRUCT.board gives a note when no part of a bought board is a module (kind or type module) and names wired = true – changing the key is the project's decision, edakit convert does not do it. One board has one of the two keys, not both.

Built on a perfboard, a hand-wired board has a board file after all: the plan of the perfboard, [perfboard], checked against the graph and drawn by edakit perfboard (7.9).

Without an edakit project

For a perfboard, an older board, a part from the bin, something made by hand, you are the datasheet. The skill own-part asks for pins, supply range and current once, has what nobody knows measured, and writes the entry with own: {"what": "pcb" | "bin" | "handmade", "notes": …}: every value with its source ("measured 2026-11-02, multimeter …, all LEDs on: 182 mA"), what is guessed as "assumption …", and the sign-off against those notes. The checks treat it like the copy of an edakit board; SYNC.source has nothing to compare it with.

Own footprints as data

State: edakit part build runs the EasyEDA calls the bell's and a real project's scripts use. It ran against EasyEDA once, on the bell's T-Display in a throwaway project: the build, the copy in the PCB, a pad changed as a line of text and the reopened project held (implementation plan, live run of 3.6).

A footprint that exists only in EasyEDA – drawn in its editor, or rebuilt there by a script of the project – is not text: the repository knows its name, pcb dump measures what was put there, and nobody can review or diff it. The parts entry can hold it as data instead, in footprint_data beside footprint. edakit builds the project's copy from it (edakit part build), holds the copy to it (SYNC.footprint), and a change of the footprint is a line of text. The Christmas bell's T-Display, rebuilt today by its script tdisplay_device.py from the copy of a 2×12 female header, as data:

"footprint": "HDR-TH_24P-P2.54-V-F-R2-C12-S2.54-2",
"footprint_data": {
 "symbol": "box",
 "origin": [0, 0],
 "pads": {
  "1": {"x": 11.43, "y": -13.97, "shape": "RECT", "w": 1.7, "h": 1.7, "hole": [1.1, 1.1]},
  "2": {"x": 11.43, "y": -11.43, "shape": "ELLIPSE", "w": 1.7, "h": 1.7, "hole": [1.1, 1.1]},
  …
  "24": {"x": -11.43, "y": 13.97, "shape": "ELLIPSE", "w": 1.7, "h": 1.7, "hole": [1.1, 1.1]}
 },
 "lines": [{"points": [[-12.83, -30.2], [12.83, -30.2], [12.83, 21.9], [-12.83, 21.9], [-12.83, -30.2]],
            "width": 0.1524},
           {"points": [[-4.6, -29.9], [4.6, -29.9], [4.6, -27.9], [-4.6, -27.9], [-4.6, -29.9]], "width": 0.1524}],
 "texts": [{"text": "USB", "x": 0, "y": -25.8, "size": 1.5, "width": 0.1524},
           {"text": "T-Display", "x": 0, "y": 18.3, "size": 1.5, "width": 0.1524}],
 "outline": [[-12.83, -30.2], [12.83, -30.2], [12.83, 21.9], [-12.83, 21.9]]
}

The right row is 1–12, the left row 13–24 at x = −11.43, pitch 2.54 mm, square pads 1 and 13 at the USB end. Lengths in mm, y up, the footprint seen from the top at 0° – the axes of pcb dump (which measures in mil) and of the library footprints edakit part import caches:

Field Content
origin optional, [x, y]: where the footprint's origin lies in these coordinates – the point [place] positions and pcb dump measures from; default [0, 0]. Lets pads be written as a datasheet dimensions them (pin 1 at 0, 0) and the origin put elsewhere
pads pad number → x, y, shape (RECT, ELLIPSE, OVAL), w, h, rot (degrees, default 0), layer (TOP, BOTTOM, MULTI; default MULTI with a hole, TOP without), hole [w, l] (a slot when l > w), plated (default true), mechanical (true on a pad whose number is no pin of the entry: a mounting pad) – the form part import caches pads in, in mm
pads_same_number optional: number → every pad of a number used several times (shell legs), as the cache has them; pads holds the first
lines silkscreen polylines {points, width}, closed when the last point is the first; width default 0.15 mm (JLCPCB prints nothing thinner)
texts silkscreen texts {text, x, y, size, width, rot}, centred on x, y
marks polarity marks {sign, x, y, size, width}: a "+" drawn as two strokes of size – as GEO.mark finds a "+" in the silk Gerber (strokes of 0.3 to 2 mm) –, a "−" as one
outline the body on the component-shape layer, [[x, y], …]: the part's extent in EasyEDA
symbol "box": the copy's symbol is rebuilt too (below); without it the carrier's symbol stays

Why beside footprint, not in it. footprint stays the name of the copy in EasyEDA: SYNC.footprint compares it with the PCB, part import writes and compares it, pcb plan, GEO.hole and part diff find the library footprint by it, and the copy keeps the carrier's footprint name (the bell's T-Display is still HDR-TH_24P-…, decision D-13 there). An object in its place would break every reader of the name – and every older edakit that reads the entry. Beside it, an entry without footprint_data is what it was, and no project changes until it adds the data. The name says which copy, the data what it looks like.

The symbol follows from pins and symbol, as edakit schematic draw draws a box (6.5): every pin of the entry on the side symbol names (the others on the right), 10 units of 10 mil long, its name and number from the entry, the body as wide as the names need. Nothing is missing for such a box. The designator prefix (U, J …) and the device's other properties stay the carrier's.

What the entry does not have is any other drawing: a diode, an LED or a transistor is a shape schematic draw makes up, not data. So "symbol": "box" is for modules, ICs and boards; for every other part the carrier's symbol stays, and its pins must be the entry's (STRUCT.entry against the library symbol, SYNC.schematic in the gate). With "symbol": "box" STRUCT.entry does not compare the graph with the carrier's library symbol: it is not the copy's.

Building the copy

  1. A library footprint becomes an own one: edakit part footprint KEY shows the footprint as data, --write puts it into the entry as footprint_data (with a line in sources); an entry that has the data is refused. The source is the dump of a placed part (<board>.geometry.json, --board picks the board), else the library footprint of edakit part import.

The dump measures places and holes – shapes and sizes only of parts that have footprint data, and older dumps not even holes –, so shape and size come from the library footprint where it has the pad at the same place; what is still missing, PARTS.footprint names ("pads 1–24: shape missing"), exit 2. Neither the dump nor the cache has the print: without lines, texts, marks and outline the build keeps the copy's print, with any of them the copy's print is exactly the data's. 2. Change the data – a pad is a line. The circuit hash changes (the parts file, byte for byte), so M-1, which checks polarity at the footprint, is done again (decision 5 of design-as-text), and the layout hash, so a sign-off of scope layout lapses; the fab hash sees the copper at the next gate run. 3. Build the copy: edakit part build KEY checks the data (PARTS.footprint) and shows what it would build – 24 pads (24 with a hole), 2 lines, 2 texts, outline, symbol: a box with 24 pins; --write builds it in EasyEDA, under the claim (--board picks the board; default the only made one).

The carrier is the device the part is placed with – its LCSC number, which for a stand-in is another part's (the bell's T-Display on the female header C18722937), or its easyeda_device – and must be on the board's template once (edakit schematic create): a device created through the API cannot be placed in the local client, the copy of a system part can.

edakit finds the carrier's copies there, opens the footprint copy in its editor, deletes its pads – and, where the data has print, the silkscreen, component-shape, marking and pin-soldering layers –, creates the data's, saves and reads the pads back; with "symbol": "box" the same for the symbol. Then it saves the project. With [scripts] the build is recorded like an easyeda script: project-library:<b> changed, the parts in the PCB and the routing from before it (project scripts, 8.6). 4. Into the PCB: placed parts keep their old copy (9.4) – delete them in the PCB, edakit pcb parts --write, edakit pcb dump (it measures the pads of parts with footprint data with shape, size, rotation and layer), then edakit check: SYNC.footprint holds the copy to the data. Then the round as usual, through route all. After the first build of a copy, edakit schematic create --save and edakit schematic layout --save come before pcb parts: in the live run of 3.6 EasyEDA could not place the rebuilt copy ("Cannot convert undefined or null to object") while the schematic's parts still carried the carrier – pcb parts names that way when EasyEDA answers so.

Footprint checks

These rule checks (3.3) run only in a project where a parts entry has footprint_data – every other project runs the checks it ran.

PARTS.footprint: the data is complete and consistent – every pad its place, shape and size, a through-hole pad its hole; every pad a pin of the entry or mechanical, every pin a pad; lines, texts, marks and outline well formed; a "+" mark nearest to a pad whose role is anode, plus or vbus, a "−" to cathode or minus (GEO.mark's test, on the text); a symbol box whose sides name pins of the entry, each once.

SYNC.footprint: the copy equals the data – the footprint the dump measured on the board has the footprint name of the entry and the data's pads: the same numbers, each within 0.02 mm of its place (SYNC.plan's tolerance; the bell's copy lies 0.0013 mm off), the same hole within 0.01 mm, and where the dump measured them shape, size, rotation and layer. SYNC.footprint runs on the dump, offline: in every edakit check, right after the dump of a round, where a stale copy is found before anything is routed on it, and in the gate's stage P.

Stage L does not repeat it: there SYNC.footprint holds the PCB's pads to the same dump within 0.05 mm – data = dump in SYNC.footprint, dump = PCB in SYNC.footprint –, and the PCB export has no holes and shapes, the dump has. The print is not measured by pcb dump; the gate sees it on the silkscreen (GEO.mark, GEO.silk).

Until the dump has the part, pcb plan, pcb place, GEO.hole and part diff take the data as the part's geometry ("footprint data", before the library footprint), so holes and leads are checked on the text already.

Not covered yet: pad shapes NGON and POLYGON (written as RECT, ELLIPSE or OVAL), arcs and circles in the print (written as points), and the print in SYNC.footprint. A slot is built along the pad's longer side, as the live run of step 3.6 showed.

4.6 Datasheets kept with the project

edakit part fetch <key> downloads the datasheet of a parts entry once and keeps it – every later reader, the sign-off, the second check M-1, a review, reads the same file. edakit part fetch --all fetches every entry that has none yet. The sources, in this order:

  1. --url URL
  2. the entry's datasheet.url
  3. for an LCSC number, pdfUrl of LCSC's product data (https://wmsc.lcsc.com/ftps/wm/product/detail?productCode=…)
  4. dataManualUrl of exactly that number in JLCPCB's catalogue (the endpoint of PARTS.stock) – where that is LCSC's datasheet page (www.lcsc.com/datasheet/lcsc_datasheet_<name>.pdf), the file behind it (wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/<name>.pdf): the page is a page of the shop since 2026, the file sends the browser on to the PDF

A page is followed once, to the part's PDF: where it sends the browser by script or meta refresh (LCSC's file), else its embedded viewer (the iframe of an item.szlcsc.com page), else a link whose address or words name the part – its mpn or LCSC number – or say datasheet (the download of a JLCPCB part page). Its other PDF links are named, not followed: the shop page links nothing but the certificates in its footer, and the first of them was once kept as the datasheet of a battery holder (chapter 17).

Only a complete PDF counts – it starts with %PDF and has its %%EOF in the last 64 kB (some tools append metadata after it). A refused request, an HTML page, a download that broke off or an expired signed link is reported with its reason, and an earlier copy stays as it is ("kept the copy of 2026-10-27", exit 2).

The copy goes to <datasheets>/<key>.pdf ([project] datasheets, default <artifacts>/datasheets: not versioned; a folder in the repository keeps them with the project) and its record to <key>.json: key, mpn, url (the source's address), pdf_url (the link a page led to), source, sha256, fetched, bytes, pages, text and names_mpn. The parts entry is not touched – a change there renews the circuit hash and lapses the sign-offs (4.3). A sign-off records the hash it read as verified.datasheet_sha256; a new download with another hash is a warning, "changed since the sign-off".

With pdftotext (poppler-utils) on PATH the text is checked; edakit doctor says whether it is there:

The text Result
names the mpn as a whole word (case ignored) ok, names_mpn: "yes"
does not name it note, "no" – some vendors never print it (the KT-0805G sheet says only "0805"); compare package, drawing and values
names it only inside a longer part number warning, the variant: "MT3608L" for MT3608 – a sheet of another part (pin 6 OC instead of NC)
has no text layer (fewer than 20 characters a page) note, text: false: scanned, read the drawings as images (pdftoppm -r 300 -png)
no pdftotext note, text: null – nothing checked