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 @@ + + + + +0 + +5 + +10 + +0 + +5 + +10 + +Time Domain — V(ibus) +V(ibus) +Time (s) + \ 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 @@ + + + + +0 + +5 + +10 + +0 + +5 + +10 + +Time Domain — V(rx) +V(rx) +Time (s) + \ 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 @@ + + + + +0 + +5 + +10 + +0 + +5 + +10 + +Time Domain — V(ibus) +V(ibus) +Time (s) + \ 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 @@ + + + + +0 + +5 + +10 + +0 + +5 + +10 + +Time Domain — V(rx) +V(rx) +Time (s) + \ No newline at end of file