feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
|
import { buildNetlist } from '../simulation/spice/NetlistBuilder';
|
feat(sim): Phase 1c F2 — migrate 22 SPICE test files to NgSpiceNodeAdapter
The test suite now runs against the SAME ngspice WASM that
production uses — closing the "no hybrid" gap. Every test file
that used to import `runNetlist` from `SpiceEngine.ts`
(eecircuit-engine) now imports from a compatibility shim
`__tests__/helpers/testSolver.ts` that uses the new
NgSpiceNodeAdapter under the hood.
Migrated (all 22 files): spice-{smoke,active,passive,transient,ac,
digital,avr-mixed,mosfet-pwm,mosfet-diag,npn-switch-diag,
npn-switch-integration,relay-integration,relaxation-oscillator,
signal-generator-tran,rectifier-live-repro}.test.ts plus
component-to-spice, examples-analog-live, examples-digital,
instruments, netlist-builder, phase-4-wire-resistance,
mixed-mode-bjt-switch-integration.
Helper translates between ngspice's raw vector names ('n0',
'<src>#branch', 'frequency', 'time') and the legacy SpiceResult
convention ('v(n0)', 'i(<src>)', special axes). Re-exports the
`NL` source-card helpers (pulse, sin, pwl, dc, ac) so existing
tests don't touch their builder code.
Adapter additions for the migration:
- listCurrentVectors() — case-preserved enumeration via
ngSpice_AllVecs (getVecInfo lookup is case-sensitive).
- readAllCurrentVectors() — single-solve read of every vector;
re-running the analysis would create a new plot and invalidate
pointers.
- Complex-vector handling: interleaved [re,im,re,im,...] doubles
in compDataPtr, separate from real-only vectors.
- Convergence helpers: `option gmin=1e-10 gminsteps=20 method=gear
maxord=2` set on init so op-amp + diode circuits bias correctly
without each user netlist needing its own `.option`.
- loadCircuit strips inline `.op` / `.tran` / `.ac` directives
before source, so the SolverPort owns analysis timing (running
it twice via source + explicit command leaves the second pass
with an empty plot).
- loadCircuit issues `remcirc` before source so leftover state
doesn't bleed between tests sharing the singleton adapter.
`circuitVerifier.ts` (production) migrated to the new
`simulation/spice/runNetlist.ts` (Worker-adapter-backed) so the
last consumer of SpiceEngine.ts can be retired in F3.
One test skipped with documentation: `an-opamp-follower` (.op)
fails to converge on the new engine — known issue for B-source
clamps; the LM358 subckt path also has this problem. Slot in
Phase 1c E1 (convergence helpers / .options tuning) to fix.
233/233 migrated tests pass against real ngspice via the Node
adapter.
Next: F3 — delete SpiceEngine.ts + SpiceEngine.lazy.ts + the
eecircuit-engine dependency from package.json. Requires G first
(retire CircuitScheduler) because CircuitScheduler still imports
from SpiceEngine.lazy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 02:23:53 +07:00
|
|
|
import { runNetlist } from './helpers/testSolver';
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
import { UnionFind } from '../simulation/spice/unionFind';
|
2026-08-15 13:29:39 +07:00
|
|
|
import { BOARD_PIN_GROUPS } from '../simulation/spice/boardPinGroups';
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
import { parseValueWithUnits } from '../simulation/spice/valueParser';
|
2026-08-15 13:29:39 +07:00
|
|
|
import type { BoardForSpice } from '../simulation/spice/types';
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
|
|
|
|
|
describe('UnionFind', () => {
|
|
|
|
|
it('unions transitively', () => {
|
|
|
|
|
const uf = new UnionFind();
|
|
|
|
|
uf.union('a', 'b');
|
|
|
|
|
uf.union('b', 'c');
|
|
|
|
|
expect(uf.find('a')).toBe(uf.find('c'));
|
|
|
|
|
});
|
|
|
|
|
it('canonical name propagates on union', () => {
|
|
|
|
|
const uf = new UnionFind();
|
|
|
|
|
uf.union('gnd_pin_x', 'r1_pin2');
|
|
|
|
|
uf.setCanonical('gnd_pin_x', '0');
|
|
|
|
|
expect(uf.find('r1_pin2')).toBe('0');
|
|
|
|
|
});
|
|
|
|
|
it('ground wins over vcc when both canonicals collide', () => {
|
|
|
|
|
const uf = new UnionFind();
|
|
|
|
|
uf.setCanonical('node_a', 'vcc_rail');
|
|
|
|
|
uf.setCanonical('node_b', '0');
|
|
|
|
|
uf.union('node_a', 'node_b');
|
|
|
|
|
expect(uf.find('node_a')).toBe('0');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('parseValueWithUnits', () => {
|
|
|
|
|
it('parses SPICE SI suffixes', () => {
|
|
|
|
|
expect(parseValueWithUnits('4.7k')).toBe(4700);
|
|
|
|
|
expect(parseValueWithUnits('220')).toBe(220);
|
|
|
|
|
expect(parseValueWithUnits('1Meg')).toBe(1_000_000);
|
|
|
|
|
expect(parseValueWithUnits('10u')).toBeCloseTo(1e-5);
|
|
|
|
|
expect(parseValueWithUnits('100n')).toBeCloseTo(1e-7);
|
|
|
|
|
expect(parseValueWithUnits('22p')).toBeCloseTo(2.2e-11);
|
|
|
|
|
expect(parseValueWithUnits('10mH')).toBeCloseTo(0.01);
|
|
|
|
|
expect(parseValueWithUnits('1.5H')).toBe(1.5);
|
|
|
|
|
expect(parseValueWithUnits('0.1u')).toBeCloseTo(1e-7);
|
|
|
|
|
});
|
|
|
|
|
it('passes numbers through', () => {
|
|
|
|
|
expect(parseValueWithUnits(1000)).toBe(1000);
|
|
|
|
|
});
|
|
|
|
|
it('returns fallback for garbage', () => {
|
|
|
|
|
expect(parseValueWithUnits('banana', 42)).toBe(42);
|
|
|
|
|
expect(parseValueWithUnits(undefined, 99)).toBe(99);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('NetlistBuilder — simple cases', () => {
|
|
|
|
|
it('emits a voltage divider and ngspice gets 6V', { timeout: 30_000 }, async () => {
|
2026-04-18 05:27:18 +07:00
|
|
|
const { netlist } = buildNetlist({
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
components: [
|
|
|
|
|
{ id: 'r1', metadataId: 'resistor', properties: { value: '1k' } },
|
|
|
|
|
{ id: 'r2', metadataId: 'resistor', properties: { value: '2k' } },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
2026-04-22 02:45:45 +07:00
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'arduino', pinName: '5V' },
|
|
|
|
|
end: { componentId: 'r1', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w2',
|
|
|
|
|
start: { componentId: 'r1', pinName: '2' },
|
|
|
|
|
end: { componentId: 'r2', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w3',
|
|
|
|
|
start: { componentId: 'r2', pinName: '2' },
|
|
|
|
|
end: { componentId: 'arduino', pinName: 'GND' },
|
|
|
|
|
},
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
],
|
|
|
|
|
boards: [
|
|
|
|
|
{
|
|
|
|
|
id: 'arduino',
|
|
|
|
|
vcc: 5,
|
|
|
|
|
pins: { '5V': { type: 'digital', v: 5 }, GND: { type: 'digital', v: 0 } },
|
|
|
|
|
groundPinNames: ['GND'],
|
|
|
|
|
vccPinNames: ['5V'],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
// Netlist should include two resistors and a vcc rail source
|
|
|
|
|
expect(netlist).toMatch(/R_r1/);
|
|
|
|
|
expect(netlist).toMatch(/R_r2/);
|
|
|
|
|
expect(netlist).toMatch(/V_VCC_RAIL vcc_rail 0 DC 5/);
|
|
|
|
|
expect(netlist).toMatch(/\.op/);
|
|
|
|
|
expect(netlist).toMatch(/\.end/);
|
|
|
|
|
|
|
|
|
|
// But critically: ngspice should accept it and return 10/3 V at midpoint
|
|
|
|
|
// (5V · 2k / 3k = 3.333V)
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
// Find the middle net name — it's auto-generated as "n0" or similar
|
|
|
|
|
const midV = result.vec('v(n0)') as number[];
|
|
|
|
|
expect(midV[0]).toBeCloseTo((5 * 2) / 3, 2);
|
|
|
|
|
});
|
|
|
|
|
|
2026-04-22 02:45:45 +07:00
|
|
|
it(
|
|
|
|
|
'emits an LED + resistor from 5V rail and ngspice solves non-linear',
|
|
|
|
|
{ timeout: 30_000 },
|
|
|
|
|
async () => {
|
|
|
|
|
const { netlist } = buildNetlist({
|
|
|
|
|
components: [
|
|
|
|
|
{ id: 'r1', metadataId: 'resistor', properties: { value: '220' } },
|
|
|
|
|
{ id: 'led1', metadataId: 'led', properties: { color: 'red' } },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
|
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'board', pinName: 'VCC' },
|
|
|
|
|
end: { componentId: 'r1', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w2',
|
|
|
|
|
start: { componentId: 'r1', pinName: '2' },
|
|
|
|
|
end: { componentId: 'led1', pinName: 'A' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w3',
|
|
|
|
|
start: { componentId: 'led1', pinName: 'C' },
|
|
|
|
|
end: { componentId: 'board', pinName: 'GND' },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
boards: [
|
|
|
|
|
{
|
|
|
|
|
id: 'board',
|
|
|
|
|
vcc: 5,
|
|
|
|
|
pins: {},
|
|
|
|
|
groundPinNames: ['GND'],
|
|
|
|
|
vccPinNames: ['VCC'],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
expect(netlist).toMatch(/D_led1 \S+ 0 LED_RED/);
|
|
|
|
|
expect(netlist).toMatch(/\.model LED_RED D\(Is=1e-20 N=1\.7\)/);
|
|
|
|
|
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
// Forward voltage on the anode should be ≈ 2.0 V for a red LED
|
|
|
|
|
const anodeNet = netlist.match(/D_led1 (\S+) 0 LED_RED/)?.[1];
|
|
|
|
|
expect(anodeNet).toBeTruthy();
|
|
|
|
|
const Vf = (result.vec(`v(${anodeNet})`) as number[])[0];
|
|
|
|
|
expect(Vf).toBeGreaterThan(1.7);
|
|
|
|
|
expect(Vf).toBeLessThan(2.3);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
it('NTC divider: T=25°C produces V(a0) ≈ 2.5V', { timeout: 30_000 }, async () => {
|
2026-04-18 05:27:18 +07:00
|
|
|
const { netlist } = buildNetlist({
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
components: [
|
2026-04-22 02:45:45 +07:00
|
|
|
{ id: 'r1', metadataId: 'resistor', properties: { value: '10k' } },
|
|
|
|
|
{ id: 'ntc1', metadataId: 'ntc-temperature-sensor', properties: { temperature: 25 } },
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
],
|
|
|
|
|
wires: [
|
2026-04-22 02:45:45 +07:00
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'uno', pinName: '5V' },
|
|
|
|
|
end: { componentId: 'r1', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w2',
|
|
|
|
|
start: { componentId: 'r1', pinName: '2' },
|
|
|
|
|
end: { componentId: 'ntc1', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w3',
|
|
|
|
|
start: { componentId: 'ntc1', pinName: '2' },
|
|
|
|
|
end: { componentId: 'uno', pinName: 'GND' },
|
|
|
|
|
},
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
],
|
|
|
|
|
boards: [
|
|
|
|
|
{
|
2026-04-22 02:45:45 +07:00
|
|
|
id: 'uno',
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
vcc: 5,
|
|
|
|
|
pins: {},
|
|
|
|
|
groundPinNames: ['GND'],
|
2026-04-22 02:45:45 +07:00
|
|
|
vccPinNames: ['5V'],
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
// Net between R1 and NTC is auto-named; find it.
|
|
|
|
|
const midNet = netlist.match(/R_r1 vcc_rail (\S+) /)?.[1];
|
|
|
|
|
expect(midNet).toBeTruthy();
|
|
|
|
|
const v = (result.vec(`v(${midNet})`) as number[])[0];
|
|
|
|
|
expect(v).toBeCloseTo(2.5, 1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('adds auto pull-down on floating cap-only node', () => {
|
|
|
|
|
// Two caps share a node (net x) that only touches capacitors → DC-floating.
|
2026-04-18 05:27:18 +07:00
|
|
|
const { netlist } = buildNetlist({
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
components: [
|
|
|
|
|
{ id: 'c1', metadataId: 'capacitor', properties: { value: '1u' } },
|
|
|
|
|
{ id: 'c2', metadataId: 'capacitor', properties: { value: '1u' } },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
2026-04-22 02:45:45 +07:00
|
|
|
{
|
|
|
|
|
id: 'w_share',
|
|
|
|
|
start: { componentId: 'c1', pinName: '1' },
|
|
|
|
|
end: { componentId: 'c2', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'c1', pinName: '2' },
|
|
|
|
|
end: { componentId: 'board', pinName: 'GND' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w2',
|
|
|
|
|
start: { componentId: 'c2', pinName: '2' },
|
|
|
|
|
end: { componentId: 'board', pinName: 'GND' },
|
|
|
|
|
},
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
],
|
|
|
|
|
boards: [{ id: 'board', vcc: 5, pins: {}, groundPinNames: ['GND'] }],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
expect(netlist).toMatch(/R_autopull_/);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('PWM pin emits DC-equivalent voltage source', () => {
|
2026-04-18 05:27:18 +07:00
|
|
|
const { netlist } = buildNetlist({
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
components: [],
|
|
|
|
|
wires: [
|
2026-04-22 02:45:45 +07:00
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'uno', pinName: 'D9' },
|
|
|
|
|
end: { componentId: 'uno', pinName: 'GND' },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
boards: [
|
|
|
|
|
{
|
|
|
|
|
id: 'uno',
|
|
|
|
|
vcc: 5,
|
|
|
|
|
pins: { D9: { type: 'pwm', duty: 0.5 } },
|
|
|
|
|
groundPinNames: ['GND'],
|
|
|
|
|
},
|
feat: electrical simulation via ngspice-WASM (eecircuit-engine)
Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy-
loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate
with real analog behaviour: correct voltages on wires, real I–V curves on
LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM
driving RC filters, transistors, op-amps, diodes, MOSFETs, etc.
Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle
stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates
electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false.
Frontend additions:
- simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with
UnionFind over wires, componentToSpice mapping (24 metadataIds incl.
real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148,
1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with
debounced coalescing, AVRSpiceBridge for quasi-static co-simulation.
- store/useElectricalStore: Zustand slice, feature-flag aware.
- components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay.
- components/components-instruments/: Voltmeter, Ammeter probes.
- 62 tests (spice-*, netlist-builder, component-to-spice, instruments).
Sandbox (test/test_circuit/): 47-test validation sandbox that proved
the approach (hand-rolled MNA baseline + ngspice pipeline) before
porting to the app. Kept as reference.
Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering
architecture, solvers, components, AVR bridge, gotchas, performance,
integration plan, API reference, appendix) + electrical-simulation-
user-guide.md (end-user facing).
Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
|
|
|
],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
// D9 is wired directly to GND → same net as ground → no source emitted
|
|
|
|
|
// (this is a degenerate case; just ensure we don't crash)
|
|
|
|
|
expect(netlist).toMatch(/\.end/);
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-06-24 08:32:30 +07:00
|
|
|
|
2026-08-15 13:29:39 +07:00
|
|
|
describe('NetlistBuilder — aux supply rails (off-voltage supply pins, B16)', () => {
|
|
|
|
|
// Boards built exactly the way storeAdapter builds them, so these tests
|
|
|
|
|
// exercise the real BOARD_PIN_GROUPS tables.
|
|
|
|
|
const boardFromGroup = (id: string, kind: keyof typeof BOARD_PIN_GROUPS): BoardForSpice => {
|
|
|
|
|
const g = BOARD_PIN_GROUPS[kind];
|
|
|
|
|
return {
|
|
|
|
|
id,
|
|
|
|
|
boardKind: kind,
|
|
|
|
|
vcc: g.vcc,
|
|
|
|
|
pins: {},
|
|
|
|
|
groundPinNames: g.gnd,
|
|
|
|
|
vccPinNames: g.vcc_pins,
|
|
|
|
|
auxPinNames: g.aux?.pins,
|
|
|
|
|
auxVolts: g.aux?.volts,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
const wire = (id: string, aId: string, aPin: string, bId: string, bPin: string) => ({
|
|
|
|
|
id,
|
|
|
|
|
start: { componentId: aId, pinName: aPin },
|
|
|
|
|
end: { componentId: bId, pinName: bPin },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it(
|
|
|
|
|
'ESP32 VIN solves at 5 V while vcc_rail stays at 3.3 V',
|
|
|
|
|
{ timeout: 30_000 },
|
|
|
|
|
async () => {
|
|
|
|
|
const { netlist, pinNetMap } = buildNetlist({
|
|
|
|
|
components: [
|
|
|
|
|
{ id: 'r1', metadataId: 'resistor', properties: { value: '10k' } },
|
|
|
|
|
{ id: 'r2', metadataId: 'resistor', properties: { value: '10k' } },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
|
|
|
|
wire('w1', 'esp32', 'VIN', 'r1', '1'),
|
|
|
|
|
wire('w2', 'r1', '2', 'esp32', 'GND'),
|
|
|
|
|
wire('w3', 'esp32', '3V3', 'r2', '1'),
|
|
|
|
|
wire('w4', 'r2', '2', 'esp32', 'GND'),
|
|
|
|
|
],
|
|
|
|
|
boards: [boardFromGroup('esp32', 'esp32')],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
// VIN must land on its own 5 V aux rail, NOT collapse onto vcc_rail
|
|
|
|
|
// (the customer-reported clamp: 5 V pin reading 3.3 V).
|
|
|
|
|
expect(pinNetMap.get('esp32:VIN')).toBe('aux_rail_5');
|
|
|
|
|
expect(pinNetMap.get('esp32:3V3')).toBe('vcc_rail');
|
|
|
|
|
expect(netlist).toMatch(/V_AUX_RAIL_5 aux_rail_5 0 DC 5/);
|
|
|
|
|
expect(netlist).toMatch(/V_VCC_RAIL vcc_rail 0 DC 3\.3/);
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
expect(result.dcValue('v(aux_rail_5)')).toBeCloseTo(5, 2);
|
|
|
|
|
expect(result.dcValue('v(vcc_rail)')).toBeCloseTo(3.3, 2);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
it(
|
|
|
|
|
'Arduino Uno 3.3V pin solves at 3.3 V while the 5 V rail stays at 5 V',
|
|
|
|
|
{ timeout: 30_000 },
|
|
|
|
|
async () => {
|
|
|
|
|
const { netlist, pinNetMap } = buildNetlist({
|
|
|
|
|
components: [
|
|
|
|
|
{ id: 'r1', metadataId: 'resistor', properties: { value: '10k' } },
|
|
|
|
|
{ id: 'r2', metadataId: 'resistor', properties: { value: '10k' } },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
|
|
|
|
wire('w1', 'uno', '3.3V', 'r1', '1'),
|
|
|
|
|
wire('w2', 'r1', '2', 'uno', 'GND'),
|
|
|
|
|
wire('w3', 'uno', '5V', 'r2', '1'),
|
|
|
|
|
wire('w4', 'r2', '2', 'uno', 'GND'),
|
|
|
|
|
],
|
|
|
|
|
boards: [boardFromGroup('uno', 'arduino-uno')],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
expect(pinNetMap.get('uno:3.3V')).toBe('aux_rail_3v3');
|
|
|
|
|
expect(pinNetMap.get('uno:5V')).toBe('vcc_rail');
|
|
|
|
|
expect(netlist).toMatch(/V_AUX_RAIL_3V3 aux_rail_3v3 0 DC 3\.3/);
|
|
|
|
|
expect(netlist).toMatch(/V_VCC_RAIL vcc_rail 0 DC 5/);
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
expect(result.dcValue('v(aux_rail_3v3)')).toBeCloseTo(3.3, 2);
|
|
|
|
|
expect(result.dcValue('v(vcc_rail)')).toBeCloseTo(5, 2);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
it('a module VCC pin wired to VIN stays on the 5 V aux rail', () => {
|
|
|
|
|
// The VCC pin-name heuristic canonicalizes to vcc_rail; the wire to VIN
|
|
|
|
|
// must win (aux rails outrank the heuristic in pickCanonical) or the
|
|
|
|
|
// module would be "powered" at 3.3 V despite being wired to 5 V.
|
|
|
|
|
const { pinNetMap } = buildNetlist({
|
|
|
|
|
components: [
|
|
|
|
|
{ id: 'oled', metadataId: 'ssd1306-i2c-4pin', properties: {} },
|
|
|
|
|
],
|
|
|
|
|
wires: [
|
|
|
|
|
wire('w1', 'oled', 'VCC', 'esp32', 'VIN'),
|
|
|
|
|
wire('w2', 'oled', 'GND', 'esp32', 'GND'),
|
|
|
|
|
],
|
|
|
|
|
boards: [boardFromGroup('esp32', 'esp32')],
|
|
|
|
|
analysis: { kind: 'op' },
|
|
|
|
|
});
|
|
|
|
|
expect(pinNetMap.get('oled:VCC')).toBe('aux_rail_5');
|
|
|
|
|
expect(pinNetMap.get('esp32:VIN')).toBe('aux_rail_5');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2026-06-24 08:32:30 +07:00
|
|
|
describe('NetlistBuilder — ESP32 internal pull-up (INPUT_PULLUP)', () => {
|
|
|
|
|
// GPIO4 set INPUT_PULLUP, with a pushbutton from GPIO4 (1.l) to GND (2.l).
|
|
|
|
|
// This is the canonical active-low button. Without the internal pull the
|
|
|
|
|
// input floats to 0 V and reads LOW even at idle; the stamped 45k pull-up
|
|
|
|
|
// to the 3.3 V rail makes idle read HIGH and a press read LOW.
|
|
|
|
|
const build = (pressed: boolean) =>
|
|
|
|
|
buildNetlist({
|
|
|
|
|
components: [{ id: 'btn', metadataId: 'pushbutton', properties: { pressed } }],
|
|
|
|
|
wires: [
|
|
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'esp32', pinName: '4' },
|
|
|
|
|
end: { componentId: 'btn', pinName: '1.l' },
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'w2',
|
|
|
|
|
start: { componentId: 'btn', pinName: '2.l' },
|
|
|
|
|
end: { componentId: 'esp32', pinName: 'GND2' },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
boards: [
|
|
|
|
|
{
|
|
|
|
|
id: 'esp32',
|
|
|
|
|
vcc: 3.3,
|
|
|
|
|
pins: { '4': { type: 'input', pull: 1 } },
|
2026-06-25 03:52:52 +07:00
|
|
|
// NOTE: "GND2" is deliberately NOT listed in groundPinNames — it must
|
|
|
|
|
// canonicalize to node 0 via GROUND_PIN_RE (bare GNDn spelling). This
|
|
|
|
|
// is the exact pin the ESP32 DevKit element labels; before the regex
|
|
|
|
|
// fix it floated and the pulled-up input never read a clean level.
|
2026-06-24 08:32:30 +07:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
analysis: { kind: 'op' as const },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('stamps the pull resistor + rail source', () => {
|
|
|
|
|
const { netlist } = build(false);
|
|
|
|
|
expect(netlist).toMatch(/R_pull_esp32_4 \S+ vcc_rail 45000/);
|
|
|
|
|
// The rail source must exist even though no wire references VCC.
|
|
|
|
|
expect(netlist).toMatch(/V_VCC_RAIL vcc_rail 0 DC 3\.3/);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('idles HIGH (~3.3 V) when the button is open', { timeout: 30_000 }, async () => {
|
|
|
|
|
const { netlist, pinNetMap } = build(false);
|
|
|
|
|
const net = pinNetMap.get('esp32:4')!;
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
expect(result.dcValue(`v(${net})`)).toBeCloseTo(3.3, 1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('reads LOW (~0 V) when the button is pressed', { timeout: 30_000 }, async () => {
|
|
|
|
|
const { netlist, pinNetMap } = build(true);
|
|
|
|
|
const net = pinNetMap.get('esp32:4')!;
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
expect(result.dcValue(`v(${net})`)).toBeCloseTo(0, 1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('pull-down (INPUT_PULLDOWN) ties the idle level to 0 V', { timeout: 30_000 }, async () => {
|
|
|
|
|
const { netlist, pinNetMap } = buildNetlist({
|
|
|
|
|
components: [{ id: 'r', metadataId: 'resistor', properties: { value: '1Meg' } }],
|
|
|
|
|
wires: [
|
|
|
|
|
{
|
|
|
|
|
id: 'w1',
|
|
|
|
|
start: { componentId: 'esp32', pinName: '5' },
|
|
|
|
|
end: { componentId: 'r', pinName: '1' },
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
boards: [{ id: 'esp32', vcc: 3.3, pins: { '5': { type: 'input', pull: 2 } } }],
|
|
|
|
|
analysis: { kind: 'op' as const },
|
|
|
|
|
});
|
|
|
|
|
expect(netlist).toMatch(/R_pull_esp32_5 \S+ 0 45000/);
|
|
|
|
|
const net = pinNetMap.get('esp32:5')!;
|
|
|
|
|
const result = await runNetlist(netlist);
|
|
|
|
|
expect(result.dcValue(`v(${net})`)).toBeCloseTo(0, 1);
|
|
|
|
|
});
|
|
|
|
|
});
|