physbox-mcp 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,147 @@
1
+ Metadata-Version: 2.4
2
+ Name: physbox-mcp
3
+ Version: 0.1.1
4
+ Summary: PhysBox: MCP - Model Context Protocol server for local simulation apps
5
+ Author-email: Tom Grek <tom.grek@gmail.com>
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: fastmcp>=2.0.0
13
+ Requires-Dist: websockets>=12.0
14
+
15
+ # PhysBox: MCP
16
+
17
+ PhysBox: MCP is a Model Context Protocol (MCP) server that enables LLMs and MCP clients (such as Claude Code or Claude Desktop) to interact programmatically with the three simulation web applications in the browser:
18
+
19
+ | Application | Production URL | Description |
20
+ |---|---|---|
21
+ | **Flux** | [flux.physbox.io](https://flux.physbox.io) | Discrete-event / system-dynamics simulation (interactive React Flow graph) |
22
+ | **Volt** | [volt.physbox.io](https://volt.physbox.io) | SPICE circuit simulation (powered by NgSpice WASM in browser) |
23
+ | **Mesh** | [mesh.physbox.io](https://mesh.physbox.io) | Rigid-body physics simulation (powered by MuJoCo WASM in browser) |
24
+
25
+ All communication is handled via JSON over WebSockets directly to the web app in your browser—no browser automation or DOM scraping is needed.
26
+
27
+ ---
28
+
29
+ ## How It Works
30
+
31
+ PhysBox: MCP functions as a local companion server that establishes a WebSocket relay on port `3142`.
32
+
33
+ When you open any of the simulation web apps, they connect directly to this WebSocket relay. When an MCP client executes a tool call, the command flows from the client to the companion server, gets forwarded to the active browser tab, and the results flow back.
34
+
35
+ ```
36
+ MCP Client (e.g. Claude Desktop)
37
+ └── spawns → physbox-mcp (stdio)
38
+ └── WebSocket Server (ws://localhost:3142)
39
+ ├── Flux
40
+ ├── Volt
41
+ └── Mesh
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Installation
47
+
48
+ Install the companion server directly from PyPI:
49
+
50
+ ```bash
51
+ pip install physbox-mcp
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Usage & Setup
57
+
58
+ ### 1. Open the Web Applications
59
+ Launch or access the simulation web applications in your web browser:
60
+ * **Flux:** [flux.physbox.io](https://flux.physbox.io)
61
+ * **Volt:** [volt.physbox.io](https://volt.physbox.io)
62
+ * **Mesh:** [mesh.physbox.io](https://mesh.physbox.io)
63
+
64
+ As soon as a page finishes loading, it automatically registers with the companion WebSocket server.
65
+
66
+ ### 2. Configure Your MCP Client
67
+
68
+ #### For Claude Desktop
69
+ Add the following block to your Claude Desktop configuration file (typically located at `AppData/Roaming/Claude/claude_desktop_config.json` on Windows or `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "physbox-mcp": {
75
+ "command": "physbox-mcp",
76
+ "args": ["--stdio"]
77
+ }
78
+ }
79
+ }
80
+ ```
81
+
82
+ #### For Claude Code
83
+ Add a `.mcp.json` file to your project root (or update your global configuration at `~/.claude/mcp.json`):
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "physbox-mcp": {
89
+ "type": "stdio",
90
+ "command": "physbox-mcp",
91
+ "args": ["--stdio"]
92
+ }
93
+ }
94
+ }
95
+ ```
96
+
97
+ #### For Google Antigravity IDE
98
+
99
+ On Windows/WSL setups, Antigravity IDE reads configuration from the global configuration directory. Because the global directory (`~/.gemini/config/`) may be write-restricted, you should link it to the writable `~/.gemini/antigravity/` folder:
100
+
101
+ 1. In PowerShell, create a **Hard Link** from the global configuration target to the writable user directory:
102
+ ```powershell
103
+ # Delete the empty placeholder file if it exists
104
+ Remove-Item -Path "$env:USERPROFILE\.gemini\config\mcp_config.json" -Force -ErrorAction SilentlyContinue
105
+
106
+ # Create a Hard Link to the writable copy
107
+ New-Item -ItemType HardLink -Path "$env:USERPROFILE\.gemini\config\mcp_config.json" -Target "$env:USERPROFILE\.gemini\antigravity\mcp_config.json"
108
+ ```
109
+
110
+ 2. Add the `physbox-mcp` WSL configuration to your `mcp_config.json` (which maps automatically to the hard-linked destination):
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "physbox-mcp": {
115
+ "command": "C:\\Windows\\system32\\wsl.exe",
116
+ "args": [
117
+ "-d",
118
+ "Ubuntu-20.04",
119
+ "/home/boab/physbox_mcp/venv/bin/python",
120
+ "/home/boab/physbox_mcp/physbox_mcp/server.py",
121
+ "--stdio"
122
+ ]
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ 3. **Restart the IDE** (or close and reload the agent chat session) to register the MCP tools natively.
129
+
130
+
131
+ ### 3. Run the Companion Server Manually (Optional)
132
+ If you are running the server in HTTP mode rather than Stdio, you can run:
133
+
134
+ ```bash
135
+ # Starts HTTP server listening on port 3141 (default)
136
+ physbox-mcp
137
+ ```
138
+
139
+ Or configure custom port parameters:
140
+ ```bash
141
+ physbox-mcp --port=4000
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Development & Contribution
147
+ For instructions on local development, modifying schemas, extending tool definitions, and manual builds, please refer to [README_DEV.md](file:///wsl.localhost/Ubuntu-20.04/home/boab/expt_mcp/README_DEV.md).
@@ -0,0 +1,133 @@
1
+ # PhysBox: MCP
2
+
3
+ PhysBox: MCP is a Model Context Protocol (MCP) server that enables LLMs and MCP clients (such as Claude Code or Claude Desktop) to interact programmatically with the three simulation web applications in the browser:
4
+
5
+ | Application | Production URL | Description |
6
+ |---|---|---|
7
+ | **Flux** | [flux.physbox.io](https://flux.physbox.io) | Discrete-event / system-dynamics simulation (interactive React Flow graph) |
8
+ | **Volt** | [volt.physbox.io](https://volt.physbox.io) | SPICE circuit simulation (powered by NgSpice WASM in browser) |
9
+ | **Mesh** | [mesh.physbox.io](https://mesh.physbox.io) | Rigid-body physics simulation (powered by MuJoCo WASM in browser) |
10
+
11
+ All communication is handled via JSON over WebSockets directly to the web app in your browser—no browser automation or DOM scraping is needed.
12
+
13
+ ---
14
+
15
+ ## How It Works
16
+
17
+ PhysBox: MCP functions as a local companion server that establishes a WebSocket relay on port `3142`.
18
+
19
+ When you open any of the simulation web apps, they connect directly to this WebSocket relay. When an MCP client executes a tool call, the command flows from the client to the companion server, gets forwarded to the active browser tab, and the results flow back.
20
+
21
+ ```
22
+ MCP Client (e.g. Claude Desktop)
23
+ └── spawns → physbox-mcp (stdio)
24
+ └── WebSocket Server (ws://localhost:3142)
25
+ ├── Flux
26
+ ├── Volt
27
+ └── Mesh
28
+ ```
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ Install the companion server directly from PyPI:
35
+
36
+ ```bash
37
+ pip install physbox-mcp
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Usage & Setup
43
+
44
+ ### 1. Open the Web Applications
45
+ Launch or access the simulation web applications in your web browser:
46
+ * **Flux:** [flux.physbox.io](https://flux.physbox.io)
47
+ * **Volt:** [volt.physbox.io](https://volt.physbox.io)
48
+ * **Mesh:** [mesh.physbox.io](https://mesh.physbox.io)
49
+
50
+ As soon as a page finishes loading, it automatically registers with the companion WebSocket server.
51
+
52
+ ### 2. Configure Your MCP Client
53
+
54
+ #### For Claude Desktop
55
+ Add the following block to your Claude Desktop configuration file (typically located at `AppData/Roaming/Claude/claude_desktop_config.json` on Windows or `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "physbox-mcp": {
61
+ "command": "physbox-mcp",
62
+ "args": ["--stdio"]
63
+ }
64
+ }
65
+ }
66
+ ```
67
+
68
+ #### For Claude Code
69
+ Add a `.mcp.json` file to your project root (or update your global configuration at `~/.claude/mcp.json`):
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "physbox-mcp": {
75
+ "type": "stdio",
76
+ "command": "physbox-mcp",
77
+ "args": ["--stdio"]
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ #### For Google Antigravity IDE
84
+
85
+ On Windows/WSL setups, Antigravity IDE reads configuration from the global configuration directory. Because the global directory (`~/.gemini/config/`) may be write-restricted, you should link it to the writable `~/.gemini/antigravity/` folder:
86
+
87
+ 1. In PowerShell, create a **Hard Link** from the global configuration target to the writable user directory:
88
+ ```powershell
89
+ # Delete the empty placeholder file if it exists
90
+ Remove-Item -Path "$env:USERPROFILE\.gemini\config\mcp_config.json" -Force -ErrorAction SilentlyContinue
91
+
92
+ # Create a Hard Link to the writable copy
93
+ New-Item -ItemType HardLink -Path "$env:USERPROFILE\.gemini\config\mcp_config.json" -Target "$env:USERPROFILE\.gemini\antigravity\mcp_config.json"
94
+ ```
95
+
96
+ 2. Add the `physbox-mcp` WSL configuration to your `mcp_config.json` (which maps automatically to the hard-linked destination):
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "physbox-mcp": {
101
+ "command": "C:\\Windows\\system32\\wsl.exe",
102
+ "args": [
103
+ "-d",
104
+ "Ubuntu-20.04",
105
+ "/home/boab/physbox_mcp/venv/bin/python",
106
+ "/home/boab/physbox_mcp/physbox_mcp/server.py",
107
+ "--stdio"
108
+ ]
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ 3. **Restart the IDE** (or close and reload the agent chat session) to register the MCP tools natively.
115
+
116
+
117
+ ### 3. Run the Companion Server Manually (Optional)
118
+ If you are running the server in HTTP mode rather than Stdio, you can run:
119
+
120
+ ```bash
121
+ # Starts HTTP server listening on port 3141 (default)
122
+ physbox-mcp
123
+ ```
124
+
125
+ Or configure custom port parameters:
126
+ ```bash
127
+ physbox-mcp --port=4000
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Development & Contribution
133
+ For instructions on local development, modifying schemas, extending tool definitions, and manual builds, please refer to [README_DEV.md](file:///wsl.localhost/Ubuntu-20.04/home/boab/expt_mcp/README_DEV.md).
@@ -0,0 +1,14 @@
1
+ import os
2
+ import re
3
+ from importlib.metadata import version, PackageNotFoundError
4
+
5
+ try:
6
+ __version__ = version("physbox-mcp")
7
+ except PackageNotFoundError:
8
+ try:
9
+ toml_path = os.path.join(os.path.dirname(__file__), "..", "pyproject.toml")
10
+ with open(toml_path, "r", encoding="utf-8") as f:
11
+ match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', f.read())
12
+ __version__ = match.group(1) if match else "0.1.0"
13
+ except Exception:
14
+ __version__ = "0.1.0"
@@ -0,0 +1,234 @@
1
+ {
2
+ "overview": "Circuit Expert is a browser-based interactive electronics circuit playground. It uses a WASM-compiled ngspice simulator (via @tscircuit/ngspice-spice-engine) to run SPICE-accurate transient simulations. The canvas is built using React Flow, where components are nodes and connections are edges. In addition to analog components, it integrates logic gates and a Javascript-sandboxed Microcontroller (MCU) simulator for hybrid digital/analog designs. Coordinate system: standard canvas coordinates (X increases right, Y increases down).",
3
+ "workflow": [
4
+ "1. Call circuit_get_schema to find the list of supported components, their parameters, and connection terminal names.",
5
+ "2. Use circuit_get_state to check the current canvas state: which nodes (components) and edges (wires) are loaded.",
6
+ "3. Construct or modify the circuit by calling circuit_set_nodes and/or circuit_set_edges.",
7
+ "4. Trigger the transient simulation using circuit_run_sim. Always call this after modifying node properties or connections to update voltages, currents, and MCU outputs.",
8
+ "5. Retrieve the simulation waveforms using circuit_get_waveforms to inspect voltages, currents, and MCU logs programmatically.",
9
+ "6. If troubleshooting or analyzing a wire, call circuit_toggle_probe to enable real-time voltage inspection at specific coordinates."
10
+ ],
11
+ "gotchas": [
12
+ "SINGULAR MATRIX ERROR: ngspice is extremely sensitive to floating nodes. Any pin or terminal that is left unconnected will cause a singular matrix error. To prevent this, the simulation layer automatically shunts unconnected pins with a 1G ohm resistor to ground, but you should always ensure loops are closed and Ground (GND) is connected to a common reference.",
13
+ "MCU TWO-PASS SIMULATION: MCUs are simulated using a two-pass mechanism. Pass 1 runs standard SPICE. Pass 2 inputs the resulting node voltages into the JS sandbox, generates a Piecewise Linear (PWL) output, and re-runs SPICE. For analog reads (e.g. analogRead('A0')), make sure the source is connected to A0 and that you call sleep() in the loop to give the simulation time steps.",
14
+ "PORT TO NET NAMES: In ngspice, nodes are resolved to net names. If you use the probe tool (circuit_toggle_probe), it maps component ports like 'led1-anode' to net names like 'net1'. Keep track of these mappings if writing custom spice.",
15
+ "SAMPLING RATES: The oscilloscope and speaker components capture waveforms over the simulation duration (e.g., simLength). If you are outputting audio, make sure the sampling frequency of your signal generator or MCU code matches the time scale to avoid aliasing.",
16
+ "NMOS HIGH-SIDE SWITCHING: Do not use NMOS as the high-side switch in a buck converter (drain at Vin, source at switching node, gate driven from a GND-referenced signal). This is a fundamental electronics constraint, not a simulator limitation: when the NMOS turns on, the source rises toward Vin, collapsing Vgs below the threshold and immediately turning it off. A proper high-side NMOS requires a bootstrap gate driver. Without one, use NMOS low-side only (source tied to GND). For buck converters, the working topology is: NMOS drain at Vin, source at the switching node, freewheeling diode cathode also at the switching node, diode anode to GND, inductor from switching node to output.",
17
+ "SWITCHING FREQUENCY AND SIMULATION SPEED: The default SPICE timestep is around 1ms (normal resolution) or 0.05ms (high resolution). Signal generators running faster than roughly 1-5kHz may require many sub-steps per cycle to converge, significantly increasing simulation time. Very high frequencies (tens of kHz and above) combined with a long simLength can make the simulation slow or unresponsive. If a switching circuit is slow to simulate, try reducing the frequency or the simLength first.",
18
+ "BUCK CONVERTER DUTY CYCLE: The theoretical duty cycle for a buck converter is Vout/Vin. In practice, MOSFET threshold voltage and diode forward drop mean the effective duty is lower — expect to tune the dutyCycle parameter down by several percent from the ideal value."
19
+ ],
20
+ "coordinateSystem": {
21
+ "canvasGrid": "React Flow canvas uses a standard X/Y coordinate system. X increases to the right, Y increases downwards.",
22
+ "spacing": "Ensure components are spaced sufficiently (typically 100-300 units apart) so that their ports can be easily connected without overlapping wires."
23
+ },
24
+ "tools": {
25
+ "circuit_get_state": "Return full Circuit Expert state: nodes, edges, isSimulating, selectedPreset, probeMode.",
26
+ "circuit_get_components": "Return all circuit components (nodes) with positions and data.",
27
+ "circuit_run_sim": "Trigger the SPICE simulation in Circuit Expert.",
28
+ "circuit_stop_sim": "Stop the running SPICE simulation.",
29
+ "circuit_toggle_probe": "Toggle probe mode (click a wire to inspect voltage).",
30
+ "circuit_load_preset": "Load a named circuit preset.",
31
+ "circuit_set_nodes": "Replace all components in the Circuit Expert canvas.",
32
+ "circuit_set_edges": "Replace all wires in the Circuit Expert canvas.",
33
+ "circuit_get_schema": "Return the Circuit Expert component schema, state fields, and SPICE model specifications.",
34
+ "circuit_get_waveforms": "Return transient waveforms (voltage/current time series) for all components and wires after simulation runs.",
35
+ "circuit_upload_audio": "Upload audio samples to a microphone node.",
36
+ "circuit_download_audio": "Download audio waveforms from a speaker node, optionally interpolated."
37
+ },
38
+ "schema": {
39
+ "nodeFields": {
40
+ "id": "string — unique component identifier",
41
+ "type": "string — component type (e.g., resistor, capacitor, inductor, diode, led, switch, multimeter, scope, speaker, transistor (npn/pnp), source (voltage/current/acvoltage/signalgen), ground, mcu, sevensegment, potentiometer, opamp, timer555)",
42
+ "position": {
43
+ "x": "number — X position on canvas",
44
+ "y": "number — Y position on canvas"
45
+ },
46
+ "data": {
47
+ "label": "string — display label (e.g., '1k', '10u', '5V')",
48
+ "value": "string/number — component value",
49
+ "isSimulating": "boolean — true if SPICE simulation is running and rendering animation",
50
+ "current_array": "number[] — transient current through component in Amperes over time",
51
+ "time_points": "number[] — transient timestamps in milliseconds",
52
+ "voltageData": "{t: number, v: number}[] — transient voltage readings (e.g., for Oscilloscope, Speaker)",
53
+ "segmentVoltageArrays": "Record<string, number[]> — per-segment voltage readings for 7-Segment displays"
54
+ }
55
+ },
56
+ "componentTerminals": {
57
+ "resistor": ["in", "out"],
58
+ "capacitor": ["in", "out"],
59
+ "inductor": ["in", "out"],
60
+ "diode": ["anode", "cathode"],
61
+ "led": ["anode", "cathode"],
62
+ "zener": ["anode", "cathode"],
63
+ "switch": ["in", "out"],
64
+ "voltage": ["pos", "neg"],
65
+ "acvoltage": ["pos", "neg"],
66
+ "signalgen": ["out", "gnd", "sync"],
67
+ "ground": ["in"],
68
+ "scope": ["ch1", "ch2", "gnd"],
69
+ "speaker": ["in", "gnd"],
70
+ "npn": ["c", "b", "e"],
71
+ "pnp": ["c", "b", "e"],
72
+ "nmos": ["d", "g", "s"],
73
+ "pmos": ["d", "g", "s"],
74
+ "potentiometer": ["in", "out", "wiper"],
75
+ "sevensegment": ["a", "b", "c", "d", "e", "f", "g", "common"],
76
+ "mcu": ["D0", "D1", "D2", "D3", "A0", "A1", "5V", "GND"],
77
+ "opamp": ["in_non", "in_inv", "vcc", "vee", "out"],
78
+ "timer555": ["1", "2", "3", "4", "5", "6", "7", "8"]
79
+ },
80
+ "spiceModels": {
81
+ "potentiometer": {
82
+ "description": "Modelled as a two-resistor voltage divider to capture 3-terminal behaviour.",
83
+ "parameters": {
84
+ "totalResistance": "number — total end-to-end resistance in ohms",
85
+ "wiperPos": "number — wiper position from 0.0 (0%) to 1.0 (100%)",
86
+ "R_top": "TotalResistance * (1 - WiperPos) ohms",
87
+ "R_bottom": "TotalResistance * WiperPos ohms"
88
+ }
89
+ },
90
+ "sevensegment": {
91
+ "description": "Common Cathode 7-segment display node. Assumes common pin is grounded.",
92
+ "activationThreshold": "2.5V — segment lights up if (V_seg - V_common) > 2.5V"
93
+ },
94
+ "currentSource": {
95
+ "description": "Provides constant DC current.",
96
+ "parameters": {
97
+ "value": "number — DC current in Amperes"
98
+ }
99
+ }
100
+ },
101
+ "probeMode": {
102
+ "description": "Allows clicking on a wire/edge after a simulation run to inspect voltage at that node.",
103
+ "behavior": "Displays a floating tooltip showing the Net name and the final voltage value of the SPICE node."
104
+ }
105
+ },
106
+ "examples": [
107
+ {
108
+ "title": "Basic LED Blinker",
109
+ "description": "A signal generator producing a 1Hz square wave to blink a red LED through a 330 ohm current-limiting resistor.",
110
+ "nodes": [
111
+ { "id": "sg1", "type": "signalgen", "position": { "x": 100, "y": 150 }, "data": { "label": "Clock 1Hz", "waveform": "square", "frequency": 1, "amplitude": 5 } },
112
+ { "id": "r1", "type": "resistor", "position": { "x": 350, "y": 150 }, "data": { "label": "330" } },
113
+ { "id": "led1", "type": "led", "position": { "x": 600, "y": 150 }, "data": { "label": "Red LED", "color": "red", "v_drop": 2.0, "max_current": 20 } },
114
+ { "id": "g1", "type": "ground", "position": { "x": 600, "y": 300 }, "data": { "label": "GND" } },
115
+ { "id": "g2", "type": "ground", "position": { "x": 100, "y": 300 }, "data": { "label": "GND" } }
116
+ ],
117
+ "edges": [
118
+ { "id": "e-sg1-r1", "source": "sg1", "target": "r1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
119
+ { "id": "e-r1-led1", "source": "r1", "target": "led1", "sourceHandle": "out", "targetHandle": "anode", "type": "smoothstep" },
120
+ { "id": "e-led1-g1", "source": "led1", "target": "g1", "sourceHandle": "cathode", "targetHandle": "in", "type": "smoothstep" },
121
+ { "id": "e-sg1-g2", "source": "sg1", "target": "g2", "sourceHandle": "gnd", "targetHandle": "in", "type": "smoothstep" }
122
+ ]
123
+ },
124
+ {
125
+ "title": "555 Timer Blinker",
126
+ "description": "An astable oscillator circuit built around the NE555 timer macro model, blinking an LED at around 1.3Hz.",
127
+ "nodes": [
128
+ { "id": "v1", "type": "voltage", "position": { "x": 100, "y": 50 }, "data": { "label": "5V" } },
129
+ { "id": "t555", "type": "timer555", "position": { "x": 400, "y": 200 }, "data": { "label": "555 Timer" } },
130
+ { "id": "r1", "type": "resistor", "position": { "x": 250, "y": 50 }, "data": { "label": "10k" } },
131
+ { "id": "r2", "type": "resistor", "position": { "x": 250, "y": 150 }, "data": { "label": "47k" } },
132
+ { "id": "c1", "type": "capacitor", "position": { "x": 250, "y": 250 }, "data": { "label": "10u" } },
133
+ { "id": "r3", "type": "resistor", "position": { "x": 600, "y": 200 }, "data": { "label": "330" } },
134
+ { "id": "led1", "type": "led", "position": { "x": 800, "y": 200 }, "data": { "label": "Blue LED", "color": "blue" } },
135
+ { "id": "g1", "type": "ground", "position": { "x": 100, "y": 400 }, "data": { "label": "GND" } }
136
+ ],
137
+ "edges": [
138
+ { "id": "e-v1-t8", "source": "v1", "target": "t555", "sourceHandle": "pos", "targetHandle": "8", "type": "smoothstep" },
139
+ { "id": "e-v1-t4", "source": "v1", "target": "t555", "sourceHandle": "pos", "targetHandle": "4", "type": "smoothstep" },
140
+ { "id": "e-v1-r1", "source": "v1", "target": "r1", "sourceHandle": "pos", "targetHandle": "in", "type": "smoothstep" },
141
+ { "id": "e-v1-g1", "source": "v1", "target": "g1", "sourceHandle": "neg", "targetHandle": "in", "type": "smoothstep" },
142
+ { "id": "e-t1-g1", "source": "t555", "target": "g1", "sourceHandle": "1", "targetHandle": "in", "type": "smoothstep" },
143
+ { "id": "e-r1-r2", "source": "r1", "target": "r2", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
144
+ { "id": "e-r1-t7", "source": "r1", "target": "t555", "sourceHandle": "out", "targetHandle": "7", "type": "smoothstep" },
145
+ { "id": "e-r2-c1", "source": "r2", "target": "c1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
146
+ { "id": "e-r2-t6", "source": "r2", "target": "t555", "sourceHandle": "out", "targetHandle": "6", "type": "smoothstep" },
147
+ { "id": "e-r2-t2", "source": "r2", "target": "t555", "sourceHandle": "out", "targetHandle": "2", "type": "smoothstep" },
148
+ { "id": "e-c1-g1", "source": "c1", "target": "g1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
149
+ { "id": "e-t3-r3", "source": "t555", "target": "r3", "sourceHandle": "3", "targetHandle": "in", "type": "smoothstep" },
150
+ { "id": "e-r3-led1", "source": "r3", "target": "led1", "sourceHandle": "out", "targetHandle": "anode", "type": "smoothstep" },
151
+ { "id": "e-led1-g1", "source": "led1", "target": "g1", "sourceHandle": "cathode", "targetHandle": "in", "type": "smoothstep" }
152
+ ]
153
+ },
154
+ {
155
+ "title": "MCU Analog Logger",
156
+ "description": "An AC voltage source monitored by an MCU analog input channel. Demonstrates the two-pass MCU SPICE simulation loop and serial logging.",
157
+ "nodes": [
158
+ { "id": "vac1", "type": "acvoltage", "position": { "x": 50, "y": 180 }, "data": { "label": "5V 40Hz", "amplitude": 5, "frequency": 40 } },
159
+ { "id": "mcu1", "type": "mcu", "position": { "x": 300, "y": 150 }, "data": { "label": "Microcontroller", "code": "pinMode('A0', 'INPUT');\n\n// Read A0 every 5ms and log it\nwhile(true) {\n const val = analogRead('A0');\n Serial.println(`t=${millis()}ms -> A0: ${val}`);\n sleep(5);\n}" } },
160
+ { "id": "g1", "type": "ground", "position": { "x": 50, "y": 300 }, "data": { "label": "GND" } },
161
+ { "id": "g2", "type": "ground", "position": { "x": 300, "y": 350 }, "data": { "label": "GND" } }
162
+ ],
163
+ "edges": [
164
+ { "id": "e-vac-mcu", "source": "vac1", "target": "mcu1", "sourceHandle": "pos", "targetHandle": "A0", "type": "smoothstep" },
165
+ { "id": "e-vac-gnd", "source": "vac1", "target": "g1", "sourceHandle": "neg", "targetHandle": "in", "type": "smoothstep" },
166
+ { "id": "e-mcu-gnd", "source": "mcu1", "target": "g2", "sourceHandle": "GND", "targetHandle": "in", "type": "smoothstep" }
167
+ ]
168
+ },
169
+ {
170
+ "title": "BJT Audio Amplifier",
171
+ "description": "A single-stage common-emitter NPN transistor amplifier amplifying a microphone input signal to drive a speaker load.",
172
+ "nodes": [
173
+ { "id": "v1", "type": "voltage", "position": { "x": 100, "y": 50 }, "data": { "label": "12V VCC" } },
174
+ { "id": "mic1", "type": "microphone", "position": { "x": 100, "y": 300 }, "data": { "label": "Mic Input" } },
175
+ { "id": "cin", "type": "capacitor", "position": { "x": 250, "y": 300 }, "data": { "label": "10u" } },
176
+ { "id": "r1", "type": "resistor", "position": { "x": 400, "y": 150 }, "data": { "label": "47k" } },
177
+ { "id": "r2", "type": "resistor", "position": { "x": 400, "y": 400 }, "data": { "label": "10k" } },
178
+ { "id": "q1", "type": "npn", "position": { "x": 600, "y": 300 }, "data": { "label": "2N3904 NPN", "bf": 300 } },
179
+ { "id": "rc", "type": "resistor", "position": { "x": 600, "y": 150 }, "data": { "label": "2.2k" } },
180
+ { "id": "re", "type": "resistor", "position": { "x": 600, "y": 450 }, "data": { "label": "1k" } },
181
+ { "id": "cout", "type": "capacitor", "position": { "x": 800, "y": 300 }, "data": { "label": "470u" } },
182
+ { "id": "spk1", "type": "speaker", "position": { "x": 1000, "y": 300 }, "data": { "label": "Speaker Output", "acCouple": true, "normalize": true } },
183
+ { "id": "g1", "type": "ground", "position": { "x": 100, "y": 600 }, "data": { "label": "GND" } }
184
+ ],
185
+ "edges": [
186
+ { "id": "e-v1-rc", "source": "v1", "target": "rc", "sourceHandle": "pos", "targetHandle": "in", "type": "smoothstep" },
187
+ { "id": "e-v1-r1", "source": "v1", "target": "r1", "sourceHandle": "pos", "targetHandle": "in", "type": "smoothstep" },
188
+ { "id": "e-v1-g1", "source": "v1", "target": "g1", "sourceHandle": "neg", "targetHandle": "in", "type": "smoothstep" },
189
+ { "id": "e-mic-cin", "source": "mic1", "target": "cin", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
190
+ { "id": "e-mic-g1", "source": "mic1", "target": "g1", "sourceHandle": "gnd", "targetHandle": "in", "type": "smoothstep" },
191
+ { "id": "e-r1-b", "source": "r1", "target": "q1", "sourceHandle": "out", "targetHandle": "b", "type": "smoothstep" },
192
+ { "id": "e-r2-b", "source": "q1", "target": "r2", "sourceHandle": "b", "targetHandle": "in", "type": "smoothstep" },
193
+ { "id": "e-r2-g1", "source": "r2", "target": "g1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
194
+ { "id": "e-cin-b", "source": "cin", "target": "q1", "sourceHandle": "out", "targetHandle": "b", "type": "smoothstep" },
195
+ { "id": "e-rc-c", "source": "rc", "target": "q1", "sourceHandle": "out", "targetHandle": "c", "type": "smoothstep" },
196
+ { "id": "e-c-cout", "source": "q1", "target": "cout", "sourceHandle": "c", "targetHandle": "in", "type": "smoothstep" },
197
+ { "id": "e-q1-re", "source": "q1", "target": "re", "sourceHandle": "e", "targetHandle": "in", "type": "smoothstep" },
198
+ { "id": "e-re-g1", "source": "re", "target": "g1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
199
+ { "id": "e-cout-spk", "source": "cout", "target": "spk1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
200
+ { "id": "e-spk-g1", "source": "spk1", "target": "g1", "sourceHandle": "gnd", "targetHandle": "in", "type": "smoothstep" }
201
+ ]
202
+ },
203
+ {
204
+ "title": "MOSFET Boost Converter",
205
+ "description": "A switching DC-DC boost converter using an NMOS switch driven by a 50kHz PWM source, stepping up 5V to approximately 25V across a filter capacitor and resistor load.",
206
+ "nodes": [
207
+ { "id": "v5v", "type": "voltage", "position": { "x": 50, "y": 250 }, "data": { "label": "5V Input" } },
208
+ { "id": "l1", "type": "inductor", "position": { "x": 250, "y": 150 }, "data": { "label": "100u" } },
209
+ { "id": "sw1", "type": "nmos", "position": { "x": 450, "y": 300 }, "data": { "label": "NMOS Switch", "vto": 2.0, "kp": 0.5 } },
210
+ { "id": "pwm1", "type": "signalgen", "position": { "x": 50, "y": 450 }, "data": { "label": "PWM 50kHz", "waveform": "square", "frequency": 50000, "amplitude": 5, "dutyCycle": 80 } },
211
+ { "id": "d1", "type": "diode", "position": { "x": 550, "y": 150 }, "data": { "label": "Schottky Diode", "v_drop": 0.3 } },
212
+ { "id": "c1", "type": "capacitor", "position": { "x": 750, "y": 250 }, "data": { "label": "100u" } },
213
+ { "id": "rload", "type": "resistor", "position": { "x": 900, "y": 250 }, "data": { "label": "1k Load" } },
214
+ { "id": "mm_out", "type": "multimeter", "position": { "x": 1000, "y": 250 }, "data": { "label": "Output Volts" } },
215
+ { "id": "g1", "type": "ground", "position": { "x": 450, "y": 550 }, "data": { "label": "GND" } }
216
+ ],
217
+ "edges": [
218
+ { "id": "e-v5v-l1", "source": "v5v", "target": "l1", "sourceHandle": "pos", "targetHandle": "in", "type": "smoothstep" },
219
+ { "id": "e-v5v-gnd", "source": "v5v", "target": "g1", "sourceHandle": "neg", "targetHandle": "in", "type": "smoothstep" },
220
+ { "id": "e-l1-sw", "source": "l1", "target": "sw1", "sourceHandle": "out", "targetHandle": "d", "type": "smoothstep" },
221
+ { "id": "e-l1-d1", "source": "l1", "target": "d1", "sourceHandle": "out", "targetHandle": "anode", "type": "smoothstep" },
222
+ { "id": "e-sw-gnd", "source": "sw1", "target": "g1", "sourceHandle": "s", "targetHandle": "in", "type": "smoothstep" },
223
+ { "id": "e-pwm-sw", "source": "pwm1", "target": "sw1", "sourceHandle": "out", "targetHandle": "g", "type": "smoothstep" },
224
+ { "id": "e-pwm-gnd", "source": "pwm1", "target": "g1", "sourceHandle": "gnd", "targetHandle": "in", "type": "smoothstep" },
225
+ { "id": "e-d1-c1", "source": "d1", "target": "c1", "sourceHandle": "cathode", "targetHandle": "in", "type": "smoothstep" },
226
+ { "id": "e-c1-rl", "source": "c1", "target": "rload", "sourceHandle": "in", "targetHandle": "in", "type": "smoothstep" },
227
+ { "id": "e-rl-mmout", "source": "rload", "target": "mm_out", "sourceHandle": "in", "targetHandle": "pos", "type": "smoothstep" },
228
+ { "id": "e-c1-gnd", "source": "c1", "target": "g1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
229
+ { "id": "e-rl-gnd", "source": "rload", "target": "g1", "sourceHandle": "out", "targetHandle": "in", "type": "smoothstep" },
230
+ { "id": "mmout-neg", "source": "mm_out", "target": "g1", "sourceHandle": "neg", "targetHandle": "in", "type": "smoothstep" }
231
+ ]
232
+ }
233
+ ]
234
+ }