diff --git a/CLAUDE.md b/CLAUDE.md index 0ca193d..52b8e84 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -57,7 +57,7 @@ Alternative (non-isolated): `reference/muki01-transistor-schematic.png` ┊ photons ┊ - I/K-Bus ←── U2 pin 4 U2 pin 1 ←── R2 (470) + I/K-Bus ←── U2 pin 4 U2 pin 1 ←── R2 (220) (collector) (LED anode) │ U2 pin 3 U2 pin 2 ←── Q1 collector (BC547) (emitter) (LED cathode) @@ -76,7 +76,7 @@ Alternative (non-isolated): `reference/muki01-transistor-schematic.png` ### TX Path (MCU -> Bus via U2 + Q1) -- TX HIGH (idle): Through R5 (470) + R3 (10k) -> Q1 base driven -> Q1 ON -> current through U2 LED via R2 (470) -> U2 phototransistor ON -> pulls bus node LOW +- TX HIGH (idle): Through R5 (470) + R3 (10k) -> Q1 base driven -> Q1 ON -> current through U2 LED via R2 (220) -> U2 phototransistor ON -> pulls bus node LOW - TX LOW (transmitting): Q1 OFF -> U2 LED OFF -> phototransistor OFF -> bus released (goes HIGH via bus pull-up) **Signal inversion:** TX HIGH -> bus LOW. This inverts the UART signal. The software must account for this, or use `SoftwareSerial` with inverted logic, or the bus idle state interpretation handles it. The muki01 library is written for this hardware — it works as-is. @@ -92,7 +92,7 @@ Bridges the bus-side 12V to the MCU-side 12V rail. This powers the MCU (via a 3. | U1, U2 | PC817 | DIP-4 | 2 | Optocoupler, CTR >= 50% at 5mA | | Q1 | BC547 | TO-92 | 1 | Or 2N3904 — TX LED driver | | R1 | 2k | 0805/TH | 1 | RX LED current limiter | -| R2 | 470 | 0805/TH | 1 | TX LED current limiter | +| R2 | 220 | 0805/TH | 1 | TX LED current limiter (reduced from 470 for 3.3V ESP32) | | R3 | 10k | 0805/TH | 1 | Q1 base resistor | | R4 | 1k | 0805/TH | 1 | RX pull-down (defines LOW when opto OFF) | | R5 | 470 | 0805/TH | 1 | TX input series resistor | @@ -263,23 +263,34 @@ mcp__mcp-ltspice__analyze_waveform(raw_file_path, signal_name, analyses) | 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):** +**TX Path (ESP32 → Bus, 3.3V VCC, R2=220Ω, 1kΩ 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 | +| V(IBUS) LOW | 0.266V | U2 phototransistor pulls bus down | +| V(IBUS) swing | 8.08V | 0.27V – 8.35V on simulated bus | +| U2 LED current | 9.71mA | Through R2 (220Ω) when Q1 ON | +| Q1 Vce(sat) | 0.073V | Fully saturated | +| Bus rise time (10-90%) | 9.1μs | 8.7% of 104.17μs bit time | | Signal inversion | Confirmed | TX HIGH → bus LOW (software must invert) | +### R2=220Ω Fix for 3.3V ESP32 + +The original muki01 design uses R2=470Ω for 5V Arduino. At 3.3V VCC, the LED current drops to 4.66mA — insufficient to saturate the phototransistor against bus impedances below ~2kΩ. Parameter sweeps revealed: + +- **R2 sweep (worst-case CTR):** R2=330Ω is the threshold; below that, V(IBUS) stays under 0.25V. R2=390Ω and above leaves saturation. +- **Bus impedance sweep (R2=470Ω):** Fails at R_PULL ≤ 510Ω (can't pull bus below 4.5V). +- **CTR grade sweep (1kΩ bus):** PC817A (Igain=1m) with R2=470Ω gives V_LOW=2.51V — marginal. PC817B+ works fine. + +**Fix: R2=220Ω** doubles the LED current (4.66→9.71mA), doubling the phototransistor base drive. This supports bus pull-ups down to ~530Ω, covering any realistic BMW I/K-Bus loading. The LED current (9.71mA) is well within the PC817's 50mA max rating. Validated simulation: `reference/tx_validated_r2_220.cir`. + ### 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. +1. **Bus rise time depends on external impedance.** The 9.1μs rise time (with 1kΩ pull-up) is 8.7% of bit time. Real BMW bus has TH3122 transceivers providing low-impedance drive, so actual rise will be similar or faster. -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. +2. **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 (at 4.7kΩ) or ~8.4V (at 1kΩ). Use a 12V supply through 100-510Ω to simulate a real bus during development. -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. +3. **PC817 grade options.** PC817A (cheapest, CTR 80-160%) works with R2=220Ω. Using PC817B+ provides extra margin with no circuit change. The sweep netlists (`reference/tx_sweep_*.cir`) document the full operating envelope. The Tucker project's validated transistor netlist is at `~/claude/tucker/k-line-board/reference/kline_esp32_validated.cir` for comparison. diff --git a/reference/ibus_tx_path.cir b/reference/ibus_tx_path.cir index 02b9397..75fbfca 100644 --- a/reference/ibus_tx_path.cir +++ b/reference/ibus_tx_path.cir @@ -41,7 +41,7 @@ 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 +R2 VCC U2_A 220 XU2 U2_A Q1C IBUS 0 PC817 Igain=1m * === RX Optocoupler (U1) for Loopback === diff --git a/reference/tx_sweep_ctr.cir b/reference/tx_sweep_ctr.cir new file mode 100644 index 0000000..3dbb633 --- /dev/null +++ b/reference/tx_sweep_ctr.cir @@ -0,0 +1,38 @@ +* TX Path - CTR Grade Sweep (Igain) with Current R2=470 and Tough Bus +* Shows how different PC817 grades perform with 1k bus pull-up +* PC817 pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter + +.param CTRgain 1m + +V_BAT V12 0 12 +V_MCU VCC 0 3.3 +V_TX TX 0 PWL(0u 0 199.5u 0 200u 3.3 512u 3.3 512.5u 0 2000u 0) +R_PULL V12 IBUS 1k +C_BUS IBUS 0 100p +R5 TX R5_R3 470 +R3 R5_R3 Q1B 10k +Q1 Q1C Q1B 0 BC547B +R2 VCC U2_A 470 +XU2 U2_A Q1C IBUS 0 PC817 Igain={CTRgain} +R1_RX IBUS U1_A 2k +XU1 U1_A 0 VCC RX PC817 Igain={CTRgain} +R4_RX RX 0 1k + +.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 + +.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) + +.step param CTRgain list 0.3m 0.5m 0.8m 1.0m 1.5m 2.3m 3.4m +.tran 0 1500u 0 0.5u +.meas tran IBUS_LOW MIN V(IBUS) +.meas tran IBUS_HIGH MAX V(IBUS) +.backanno +.end diff --git a/reference/tx_sweep_r2.cir b/reference/tx_sweep_r2.cir new file mode 100644 index 0000000..54c0d68 --- /dev/null +++ b/reference/tx_sweep_r2.cir @@ -0,0 +1,40 @@ +* TX Path - R2 Sweep at Worst-Case CTR (Igain=0.5m ~ PC817A min) +* Finding optimal R2 for 3.3V ESP32 design +* PC817 pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter + +.param R2val 470 + +V_BAT V12 0 12 +V_MCU VCC 0 3.3 +V_TX TX 0 PWL(0u 0 199.5u 0 200u 3.3 512u 3.3 512.5u 0 2000u 0) +R_PULL V12 IBUS 4.7k +C_BUS IBUS 0 100p +R5 TX R5_R3 470 +R3 R5_R3 Q1B 10k +Q1 Q1C Q1B 0 BC547B +R2 VCC U2_A {R2val} +XU2 U2_A Q1C IBUS 0 PC817 Igain=0.5m +R1_RX IBUS U1_A 2k +XU1 U1_A 0 VCC RX PC817 Igain=0.5m +R4_RX RX 0 1k + +.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 + +.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) + +.step param R2val list 100 150 220 270 330 390 470 +.tran 0 1500u 0 0.5u +.meas tran IBUS_LOW MIN V(IBUS) +.meas tran IBUS_HIGH MAX V(IBUS) +.meas tran LED_CURRENT MAX I(R2) +.meas tran BUS_RISE TRIG V(IBUS) VAL=0.5 RISE=1 TARG V(IBUS) VAL=3.5 RISE=1 +.backanno +.end diff --git a/reference/tx_sweep_rpull.cir b/reference/tx_sweep_rpull.cir new file mode 100644 index 0000000..65bb4e1 --- /dev/null +++ b/reference/tx_sweep_rpull.cir @@ -0,0 +1,39 @@ +* TX Path - Bus Impedance Sweep at Current R2=470 and Typical CTR +* Finding where the design breaks with different bus loading +* PC817 pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter + +.param Rpull 4700 + +V_BAT V12 0 12 +V_MCU VCC 0 3.3 +V_TX TX 0 PWL(0u 0 199.5u 0 200u 3.3 512u 3.3 512.5u 0 2000u 0) +R_PULL V12 IBUS {Rpull} +C_BUS IBUS 0 100p +R5 TX R5_R3 470 +R3 R5_R3 Q1B 10k +Q1 Q1C Q1B 0 BC547B +R2 VCC U2_A 470 +XU2 U2_A Q1C IBUS 0 PC817 Igain=1m +R1_RX IBUS U1_A 2k +XU1 U1_A 0 VCC RX PC817 Igain=1m +R4_RX RX 0 1k + +.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 + +.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) + +.step param Rpull list 510 1000 2200 3300 4700 6800 10000 +.tran 0 1500u 0 0.5u +.meas tran IBUS_LOW MIN V(IBUS) +.meas tran IBUS_HIGH MAX V(IBUS) +.meas tran BUS_RISE TRIG V(IBUS) VAL=0.5 RISE=1 TARG V(IBUS) VAL=3.5 RISE=1 +.backanno +.end diff --git a/reference/tx_validated_r2_220.cir b/reference/tx_validated_r2_220.cir new file mode 100644 index 0000000..cc5b6a4 --- /dev/null +++ b/reference/tx_validated_r2_220.cir @@ -0,0 +1,82 @@ +* BMW I/K-Bus Interface - TX Path VALIDATED (R2=220 fix for 3.3V ESP32) +* Tests worst realistic scenario: PC817A typical CTR + 1k bus pull-up +* Also includes RX loopback to verify full signal path +* +* Fix: R2 reduced from 470 to 220 ohms +* Effect: LED current increases from 4.66mA to ~9.3mA +* Phototransistor base drive doubles: 9.3uA -> 18.6uA +* Max collector current: 11.2mA -> 22.4mA +* Supports bus pull-ups down to ~530 ohms +* +* 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 (byte 0x68 = RAD address) === +* Inverted UART: 0V=idle, 3.3V=pulling bus LOW +* 0x68 = 01101000, LSB first = 00010110 +* 3 ones -> even parity = 1 +* Frame: START(1), 0,0,0,1,0,1,1,0, P(1), STOP(0) +* (inverted: start=HIGH, data inverted, stop=LOW) +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: 1k pull-up (loaded bus, worst realistic case) === +R_PULL V12 IBUS 1k +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) === +* FIX: R2 changed from 470 to 220 for 3.3V ESP32 compatibility +R2 VCC U2_A 220 +XU2 U2_A Q1C IBUS 0 PC817 Igain=1m + +* === RX Optocoupler (U1) for Loopback === +R1_RX IBUS U1_A 2k +XU1 U1_A 0 VCC RX PC817 Igain=1m +R4_RX RX 0 1k + +* === PC817 Subcircuit === +.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 + +* === BC547B 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 U2_LED_CURRENT MAX I(R2) +.meas tran Q1_VCE_SAT MIN V(Q1C) +.meas tran RX_LOOPBACK_HIGH MAX V(RX) +.meas tran RX_LOOPBACK_LOW MIN V(RX) +.meas tran BUS_RISE TRIG V(IBUS) VAL=1.0 RISE=1 TARG V(IBUS) VAL=7.0 RISE=1 +.meas tran BUS_FALL TRIG V(IBUS) VAL=7.0 FALL=1 TARG V(IBUS) VAL=1.0 FALL=1 +.backanno +.end