Every component validated by at least one test across the two pipelines. "JS API" is the hand-rolled MNA pipeline; "SPICE card" is the ngspice netlist syntax.
## Passive
### Resistor
| | |
|---|---|
| JS API | `new Resistor(name, nodeA, nodeB, resistance)` |
| Limitation (JS model) | Doesn't capture deep saturation; `V_CE,sat` measures ~0.7 V instead of 0.1–0.3 V |
| Recommendation | For accurate BJT work, use the ngspice pipeline with Gummel-Poon parameters |
### MOSFET (ngspice only)
| | |
|---|---|
| SPICE | `M1 d g s b NMOS_L1 L=1u W=100u` + `.model NMOS_L1 NMOS(Level=1 Vto=1.0 Kp=50u Lambda=0.01)` |
| Model level | 1 (Shichman-Hodges): `I_d = (Kp · W/L) · ((V_gs − V_th) · V_ds − V_ds²/2)` for linear region |
| Higher levels | Level 3, BSIM3/4 available in full ngspice; not all compiled into WASM build |
| Tested in | `spice_active.test.js` (switch ON/OFF) |
## Controlled sources (SPICE only)
| Card | Type | Example |
|---|---|---|
| `Ename plus minus ctrl+ ctrl− gain` | VCVS (ideal op-amp) | `Eopa out 0 inp inm 1e6` |
| `Gname plus minus ctrl+ ctrl− gm` | VCCS | `Gtc out 0 in 0 1m` |
| `Hname plus minus Vsense gain` | CCVS | Needs a 0 V source to sense current |
| `Fname plus minus Vsense gain` | CCCS | |
We use VCVS extensively for behavioral op-amp modeling. See `spice_active.test.js` (inverting amplifier) and `spice_555_astable.test.js` (Schmitt via `Bopa` limited to 0..5 V by `limit()`).
## Behavioral sources (SPICE only — **key to mixed-signal**)
The `B` card computes a voltage (or current) from an arbitrary expression:
| Pushbutton | `Switch` toggled between open/closed |
| Potentiometer | `Potentiometer` with UI-driven `wiperPos` |
| Microphone / piezo | `CurrentSource` or `VoltageSource` with PWL waveform |
| Encoder / quadrature | Two digital pins toggled by UI logic (outside the SPICE solver) |
## Integrated circuits (not yet modeled)
For the Velxio integration, these will need either ngspice `.subckt` macromodels (many available in vendor-provided SPICE libraries) or behavioral B-source blocks:
- 555 timer — vendor .subckt or our relaxation-osc behavioral model
The following mappers were added during fase 9 (commit fase 9.0–9.5) and are live in `frontend/src/simulation/spice/componentToSpice.ts`. The `MAPPERS` table now has 58 entries (up from 25) and `components-metadata.json` has 92 parts (up from 48). Every new component is accessible from the picker UI.
### Fase 9.1 — Logic gates (behavioral B-sources)
Every gate uses an ngspice B-source with `u()` unit-step functions and a 1 MΩ pull-down on the output (to give the node a DC path and prevent "matrix singular" errors).
| metadataId | Inputs | Output expression |
|---|---|---|
| `logic-gate-and` | A, B | `Vcc · u(V(A)−T) · u(V(B)−T)` |
| `logic-gate-or` | A, B | `Vcc · (1 − (1−u(V(A)−T))·(1−u(V(B)−T)))` |
| `logic-gate-nand` | A, B | `Vcc · (1 − u(V(A)−T)·u(V(B)−T))` |
| `logic-gate-nor` | A, B | `Vcc · (1−u(V(A)−T)) · (1−u(V(B)−T))` |
| `logic-gate-xor` | A, B | `Vcc · (u(V(A)−T) + u(V(B)−T) − 2·u(V(A)−T)·u(V(B)−T))` |
Threshold `T = Vcc/2`. Multi-input variants (AND-3/4, OR-3/4, NAND-3/4, NOR-3/4) extend the product/sum to more terms.
### Fase 9.2 — Transistors (discrete real parts)
NMOS and PMOS use **Level=1** Shichman-Hodges with numerically sane W/L — the previous Level=3 with `W=0.1` (= 100 mm!) caused ngspice to hang. See [`circuit-emulation-gotchas.md`](circuit-emulation-gotchas.md#mosfet-convergence).
All op-amps use a behavioral `B_out = max(vLo, min(vHi, A · (V+ − V−)))` with rails derived from `ctx.vcc`. High input impedance via 10 MΩ (or 1 TΩ for JFET input) resistors to ground on each input pin.
| metadataId | Type | Gain A | Low rail | High rail | Notes |
| `photodiode` | Regular diode + current source: `I_ph = lux · 100 nA` sinking from cathode to anode |
| `logic-gate-{and,or,nand,nor}-{3,4}` | Same behavioral pattern as 2-input gates, extended to 3 or 4 inputs |
### Fase 10 — Electromechanical + IC packaging (implemented)
#### Relay (SPDT)
| metadataId | Topology |
|---|---|
| `relay` | R + L in parallel for the coil + ngspice `S` switches for NO/NC contacts with native Vt/Vh hysteresis + B-source inverter to implement the normally-closed switch (ngspice SW has no "NC" mode). Optional integrated flyback diode (cathode on COIL+, anode on COIL−). Configurable via `coil_voltage`, `coil_resistance`, `include_flyback` properties. |
#### Optocouplers
Pattern: LED + 0 V current-sense source in series + CCCS (`F` element) mirrors I_LED into the phototransistor output with the part's Current Transfer Ratio (CTR).
| metadataId | CTR |
|---|---|
| `opto-4n25` | 0.5 (50%) |
| `opto-pc817` | 1.0 (100% typical, 80–600% spread in real parts) |
First mapper pattern in the project that emits **multiple** B-source cards per component (one per internal gate). Pin naming follows the datasheet (e.g. 1A/1B/1Y for gate 1, up to 4Y on quad packages or 6Y on hex inverters).
| metadataId | Contents |
|---|---|
| `ic-74hc00` | 4× 2-input NAND |
| `ic-74hc02` | 4× 2-input NOR |
| `ic-74hc04` | 6× NOT |
| `ic-74hc08` | 4× 2-input AND |
| `ic-74hc14` | 6× Schmitt-trigger NOT (hysteresis via state-dependent threshold) |
| `ic-74hc32` | 4× 2-input OR |
| `ic-74hc86` | 4× 2-input XOR |
Unwired gates are skipped silently (no wasted netlist cards).
#### Flip-flops (digital simulation only)
SPICE can't do edge detection in `.op` without `ddt()`, so flip-flops live in the digital-sim layer (`PartSimulationRegistry`) and **have no SPICE mapper**. They still participate in MCU-driven circuits.
Implemented via a shared `edgeTriggeredFF` helper that tracks the previous CLK state, detects rising edges, and samples the data inputs.
#### L293D dual H-bridge motor driver
| metadataId | Topology |
|---|---|
| `motor-driver-l293d` | Per channel (2 channels, EN1 + IN1/IN2 + OUT1/OUT2 and EN2 + IN3/IN4 + OUT3/OUT4): `OUT = u(EN−T) · u(IN−T) · V(VCC2)`. When EN=LOW the outputs are high-impedance (weak 10 MΩ pull-down to 0). Resolves V_motor from the wired VCC2 net when available, else from `ctx.vcc`. |
## The `_customComponents` mechanism
Velxio-specific parts (everything not defined in `wokwi-libs/wokwi-elements`) are declared in `scripts/component-overrides.json` under the `_customComponents[]` array. The metadata generator ([`scripts/generate-component-metadata.ts`](../../scripts/generate-component-metadata.ts), function `applyOverrides`) injects them before the standard property-patching loop. An entry must have: `id`, `tagName`, `name`, `category`, `pinCount`, `tags` — other fields default.
Example:
```json
{
"_customComponents": [
{
"id": "logic-gate-xnor",
"tagName": "wokwi-logic-xnor",
"name": "XNOR Gate",
"category": "logic",
"properties": [],
"defaultValues": {},
"pinCount": 3,
"tags": ["logic", "gate", "xnor", "digital"]
}
]
}
```
A drift detector at [`test/test_circuit/test/metadata_drift.test.js`](../../test/test_circuit/test/metadata_drift.test.js) fails if `components-metadata.json` is out of sync with the overrides file. The frontend CI workflow also regenerates and checks `git diff` on the JSON. Run `cd frontend && npm run generate:metadata` after any change to `component-overrides.json`.
- **Behavioral R** (resistor whose value is an expression of another node's voltage) — supported by ngspice via the `R1 a b R='expr'` syntax. Would simplify the photoresistor case.