diff --git a/CLAUDE.md b/CLAUDE.md
index 6e775f8..0ca193d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -244,12 +244,42 @@ mcp__mcp-ltspice__get_waveform(raw_file_path, signal_names, max_points)
mcp__mcp-ltspice__analyze_waveform(raw_file_path, signal_name, analyses)
```
-**Not yet simulated.** The optocoupler circuit needs a PC817 SPICE model with CTR curve and switching time parameters. A generic model:
-```spice
-* PC817 optocoupler (simplified)
-* LED: forward voltage ~1.2V at 5mA
-* Phototransistor: CTR ~100% at 5mA, rise ~4us, fall ~3us
-```
+**Simulated and validated.** Uses LTspice's built-in PC817 subcircuit (from `lib/sub/PC817.sub`) with `Igain=1m` (PC817A, ~100% CTR). Two netlists cover both signal paths:
+
+- `reference/ibus_rx_path.cir` — RX path: bus byte 0x50 at 9600 baud → U1 PC817 → ESP32 RX
+- `reference/ibus_tx_path.cir` — TX path: ESP32 TX → Q1 BC547B → U2 PC817 → bus (with RX loopback)
+
+**PC817 SPICE pin order:** `1=Anode, 2=Cathode, 3=Collector, 4=Emitter` — subcircuit uses a VCCS (G1) with `{Igain}` parameter to model optical coupling.
+
+### Simulation Results
+
+**RX Path (Bus → ESP32, 3.3V VCC):**
+
+| Measurement | Value | Notes |
+|-------------|-------|-------|
+| V(RX) HIGH | 3.128V | > 2.475V ESP32 VIH threshold |
+| V(RX) LOW | ~0V | < 0.825V ESP32 VIL threshold |
+| LED current | 5.21mA | Through R1 (2k) when bus at 12V |
+| Rise time (10-90%) | 5.8μs | 5.6% of 104.17μs bit time |
+| Signal polarity | Preserved | Bus HIGH → RX HIGH (emitter-follower) |
+
+**TX Path (ESP32 → Bus, 3.3V VCC, 4.7kΩ bus pull-up):**
+
+| Measurement | Value | Notes |
+|-------------|-------|-------|
+| V(IBUS) LOW | 0.167V | U2 phototransistor pulls bus down |
+| U2 LED current | 4.66mA | Through R2 (470Ω) when Q1 ON |
+| Q1 Vce(sat) | 0.048V | Fully saturated |
+| Bus rise time (10-90%) | 19.0μs | Passive pull-up limited; real bus is faster |
+| Signal inversion | Confirmed | TX HIGH → bus LOW (software must invert) |
+
+### Design Notes from Simulation
+
+1. **3.3V works but is marginal for TX.** At worst-case CTR (50%, PC817A grade), the phototransistor may not fully pull the bus LOW against pull-up impedances below ~2kΩ. The original 5V Arduino design has 70% more LED headroom. Consider using PC817C/D (higher CTR) or reducing R2 to 330Ω for more LED current.
+
+2. **Bus rise time depends on external impedance.** The 19μs rise time (with 4.7kΩ pull-up) is 18% of bit time — acceptable for 9600 baud. Real BMW bus has TH3122 transceivers providing low-impedance drive, so actual rise will be faster.
+
+3. **Bench testing needs a low-impedance 12V source.** U1's RX LED (through R1=2kΩ) loads the bus continuously. With only a passive pull-up, IBUS sags to ~4.3V. Use a 12V supply through 100-510Ω to simulate a real bus during development.
The Tucker project's validated transistor netlist is at `~/claude/tucker/k-line-board/reference/kline_esp32_validated.cir` for comparison.
@@ -266,14 +296,14 @@ The repo includes an extensive documentation collection (`Docs/`):
2. Protocol differences documented (vs OBD-II K-line for Tucker project)
3. Software reference identified (IbusSerial library, E46 command codes)
4. Module address map and message format documented
+5. **SPICE simulation validated** — both RX and TX paths simulated with LTspice PC817 model at 9600 baud. RX path gives clean 0-3.13V logic at 3.3V VCC. TX path confirms signal inversion and 0.17V bus LOW. Rise/fall times within 9600 baud budget.
## What's Next
-1. **Simulate** optocoupler circuit with mcp-ltspice (need PC817 model)
-2. **Port IbusSerial** library to ESP32 (replace AVR Timer2, adapt pin config)
-3. **Breadboard** prototype with PC817 + BC547 + ESP32
-4. **Test** on BMW E46 K-Bus (CD changer connector in trunk)
-5. **Build** command library for target features (lights, locks, windows)
+1. **Port IbusSerial** library to ESP32 (replace AVR Timer2, adapt pin config)
+2. **Breadboard** prototype with PC817 + BC547 + ESP32
+3. **Test** on BMW E46 K-Bus (CD changer connector in trunk)
+4. **Build** command library for target features (lights, locks, windows)
## Safety Notes
diff --git a/reference/ibus_rx_path.cir b/reference/ibus_rx_path.cir
new file mode 100644
index 0000000..4447417
--- /dev/null
+++ b/reference/ibus_rx_path.cir
@@ -0,0 +1,65 @@
+* BMW I/K-Bus Interface - RX Path (Bus to ESP32) - PC817 Optocoupler
+* Bus drives byte 0x50 (MFL addr) at 9600 baud 8E1
+* 0x50 = 01010000, LSB first = 00001010, even parity = 0
+* Frame: START(0) d0(0) d1(0) d2(0) d3(0) d4(1) d5(0) d6(1) d7(0) P(0) STOP(1)
+* Bus: 12V=idle/HIGH, 0V=active/LOW, bit time=104.17us
+* ESP32 GPIO thresholds: LOW < 0.825V, HIGH > 2.475V
+*
+* PC817 pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter
+* U1 config: emitter-follower (collector to VCC, emitter to RX output)
+
+* === Power Supplies ===
+V_BAT V12 0 12
+V_MCU VCC 0 3.3
+
+* === Bus Model ===
+* External module driving byte 0x50 through 100R source impedance
+V_BUS V_BUS_SRC 0 PWL(
++ 0u 12
++ 199.5u 12
++ 200u 0
++ 720.33u 0
++ 720.83u 12
++ 824.5u 12
++ 825u 0
++ 928.67u 0
++ 929.17u 12
++ 1032.83u 12
++ 1033.33u 0
++ 1241.17u 0
++ 1241.67u 12
++ 2000u 12)
+R_BUS V_BUS_SRC IBUS 100
+C_BUS IBUS 0 100p
+
+* === RX Optocoupler (U1) ===
+* Bus HIGH (12V): LED on -> phototransistor ON -> RX HIGH
+* Bus LOW (0V): LED off -> phototransistor OFF -> RX LOW (via R4)
+R1 IBUS U1_A 2k
+XU1 U1_A 0 VCC RX PC817 Igain=1m
+R4 RX 0 1k
+
+* === PC817 Subcircuit (inlined from LTspice library) ===
+.subckt PC817 1 2 3 4
+R1 N003 2 2
+D1 1 N003 LD
+G1 3 N004 N003 2 {Igain}
+C1 1 2 18p
+Q1 3 N004 4 [4] NP
+.model LD D(Is=1e-20 Cjo=18p)
+.model NP NPN(Bf=1200 Vaf=140 Ikf=100m Rc=1 Cjc=19p Cje=7p Cjs=7p C2=3e-15)
+.ends PC817
+
+* === Simulation ===
+.tran 0 2000u 0 0.1u
+
+* === Measurements ===
+.meas tran RX_HIGH MAX V(RX)
+.meas tran RX_LOW MIN V(RX)
+.meas tran IBUS_HIGH MAX V(IBUS)
+.meas tran IBUS_LOW MIN V(IBUS)
+.meas tran LED_CURRENT_MAX MAX I(R1)
+.meas tran RX_RISE TRIG V(RX) VAL=0.825 RISE=1 TARG V(RX) VAL=2.475 RISE=1
+.meas tran RX_FALL TRIG V(RX) VAL=2.475 FALL=1 TARG V(RX) VAL=0.825 FALL=1
+.backanno
+.end
diff --git a/reference/ibus_tx_path.cir b/reference/ibus_tx_path.cir
new file mode 100644
index 0000000..02b9397
--- /dev/null
+++ b/reference/ibus_tx_path.cir
@@ -0,0 +1,80 @@
+* BMW I/K-Bus Interface - TX Path (ESP32 to Bus) - PC817 + BC547
+* MCU TX drives Q1 (BC547B) which drives U2 (PC817) LED
+* U2 phototransistor pulls bus LOW against 4.7k pull-up to 12V
+* Test pattern: alternating 3-bit-time (312us) pulses
+* TX 0V = bus idle (HIGH), TX 3.3V = bus active (LOW via U2)
+* Includes U1 RX path for loopback observation
+* Signal inversion: TX HIGH -> bus LOW (documented in design)
+*
+* PC817 pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter
+
+* === Power Supplies ===
+V_BAT V12 0 12
+V_MCU VCC 0 3.3
+
+* === TX Test Signal ===
+* 0V=idle (U2 OFF, bus HIGH), 3.3V=active (U2 ON, bus LOW)
+* Alternating ~3 bit times (312us) to show inversion
+V_TX TX 0 PWL(
++ 0u 0
++ 199.5u 0
++ 200u 3.3
++ 512u 3.3
++ 512.5u 0
++ 825u 0
++ 825.5u 3.3
++ 1137u 3.3
++ 1137.5u 0
++ 2000u 0)
+
+* === Bus Model ===
+* Idle pull-up: represents combined impedance of other modules' inputs
+* No active driver — only U2 phototransistor drives the bus here
+R_PULL V12 IBUS 4.7k
+C_BUS IBUS 0 100p
+
+* === TX Driver: Q1 (BC547B) ===
+R5 TX R5_R3 470
+R3 R5_R3 Q1B 10k
+Q1 Q1C Q1B 0 BC547B
+
+* === TX Optocoupler (U2) ===
+* LED path: VCC -> R2 -> U2 anode(1) -> U2 cathode(2) -> Q1 collector
+* Phototransistor: collector(3) = IBUS, emitter(4) = GND
+R2 VCC U2_A 470
+XU2 U2_A Q1C IBUS 0 PC817 Igain=1m
+
+* === RX Optocoupler (U1) for Loopback ===
+* Shows what the MCU sees on RX when it transmits
+R1_RX IBUS U1_A 2k
+XU1 U1_A 0 VCC RX PC817 Igain=1m
+R4_RX RX 0 1k
+
+* === PC817 Subcircuit (inlined from LTspice library) ===
+.subckt PC817 1 2 3 4
+R1 N003 2 2
+D1 1 N003 LD
+G1 3 N004 N003 2 {Igain}
+C1 1 2 18p
+Q1 3 N004 4 [4] NP
+.model LD D(Is=1e-20 Cjo=18p)
+.model NP NPN(Bf=1200 Vaf=140 Ikf=100m Rc=1 Cjc=19p Cje=7p Cjs=7p C2=3e-15)
+.ends PC817
+
+* === Transistor Model ===
+.model BC547B NPN(IS=2.39E-14 NF=1.008 ISE=3.545E-15 NE=1.541 BF=294.3 IKF=0.1357 VAF=63.2 NR=1.004 ISC=6.272E-14 NC=1.243 BR=7.946 IKR=0.1144 VAR=25.9 RB=1 IRB=1.00E-06 RBM=1 RE=0.4683 RC=0.85 XTB=0 EG=1.11 XTI=3 CJE=1.358E-11 VJE=0.65 MJE=0.3279 TF=4.391E-10 XTF=120 VTF=2.643 ITF=0.7495 PTF=0 CJC=3.728E-12 VJC=0.3997 MJC=0.2955 XCJC=0.6193 TR=1.00E-32 CJS=0 VJS=0.75 MJS=0.333 FC=0.9579 Vceo=45 Icrating=100m mfg=NXP)
+
+* === Simulation ===
+.tran 0 2000u 0 0.1u
+
+* === Measurements ===
+.meas tran IBUS_HIGH MAX V(IBUS)
+.meas tran IBUS_LOW MIN V(IBUS)
+.meas tran IBUS_SWING PP V(IBUS)
+.meas tran TX_TO_BUS_DELAY TRIG V(TX) VAL=1.65 RISE=1 TARG V(IBUS) VAL=6 FALL=1
+.meas tran U2_LED_CURRENT MAX I(R2)
+.meas tran RX_LOOPBACK_HIGH MAX V(RX)
+.meas tran RX_LOOPBACK_LOW MIN V(RX)
+.meas tran Q1_BASE_CURRENT MAX I(R3)
+.backanno
+.end
diff --git a/reference/rx_path_vibus.svg b/reference/rx_path_vibus.svg
new file mode 100644
index 0000000..d1cf098
--- /dev/null
+++ b/reference/rx_path_vibus.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/reference/rx_path_vrx.svg b/reference/rx_path_vrx.svg
new file mode 100644
index 0000000..d24be95
--- /dev/null
+++ b/reference/rx_path_vrx.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/reference/tx_path_vibus.svg b/reference/tx_path_vibus.svg
new file mode 100644
index 0000000..74a9063
--- /dev/null
+++ b/reference/tx_path_vibus.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/reference/tx_path_vrx_loopback.svg b/reference/tx_path_vrx_loopback.svg
new file mode 100644
index 0000000..16fa89f
--- /dev/null
+++ b/reference/tx_path_vrx_loopback.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file