Circuit Design
BMW I/K-Bus interface for ESP32, galvanically isolated via PC817 optocouplers. Based on the muki01/I-K_Bus optocoupler schematic, modified for 3.3V ESP32 operation (R2: 470 to 220 ohm).
Circuit Overview
Section titled “Circuit Overview”The interface sits between a 12V automotive bus (BMW I-Bus or K-Bus) and a 3.3V ESP32 microcontroller. Four key elements:
- U1 (PC817) — RX optocoupler. Converts 12V bus signals to 3.3V logic for the ESP32’s UART RX pin. Emitter-follower configuration preserves signal polarity.
- U2 (PC817) — TX optocoupler. Converts 3.3V logic from the ESP32’s UART TX pin to open-collector drive on the 12V bus. Signal is inverted.
- Q1 (BC547) — NPN transistor. Drives U2’s infrared LED. The ESP32 cannot source enough current through its GPIO to drive the LED directly; Q1 provides the current gain.
- D1 (1N4007) — Bridges bus-side 12V to the MCU power rail (through a 3.3V regulator). Blocks reverse current when the MCU is USB-powered during development.
Galvanic isolation is maintained across both optocouplers. The only electrical connection between bus-side and MCU-side ground planes is through the power supply path (D1 + regulator). Signal paths are optically coupled.
Schematic
Section titled “Schematic” I/K-Bus Side (12V) MCU Side (3.3V) ================== ================
12V ────── D1 (1N4007) ──────────────── 12V (to regulator) (reverse protection)
I/K-Bus ─── R1 (2k) ──> U1 pin 1 U1 pin 4 <── 3.3V (LED anode) (PC817) (collector) GND <── U1 pin 2 U1 pin 3 ──> RX_Pin (LED cathode) (emitter) R4 (1k) pull-down to GND
: photons :
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) | Q1 base <── R3 (10k) <── R5 (470) <── TX_Pin GND Q1 emitter ──> GNDReference images in reference/:
muki01-optocoupler-schematic.png— original schematic from muki01muki01-transistor-schematic.png— non-isolated alternative (not used here)
RX Path (Bus to MCU via U1)
Section titled “RX Path (Bus to MCU via U1)”U1 converts 12V bus signals to 3.3V logic. The phototransistor is wired in emitter-follower configuration: collector tied to VCC (3.3V), emitter to the RX pin, with R4 (1k) pulling the emitter to ground.
Bus HIGH (12V idle): Current flows through R1 (2k) into U1’s LED. I_LED = (12V - 1.2V) / 2k = 5.4mA. At this drive level the PC817’s CTR (minimum 80% for grade A) produces sufficient base current to saturate the phototransistor. The emitter follows the collector voltage, pulling RX toward VCC. Result: RX = HIGH.
Bus LOW (module transmitting): No voltage across R1, no LED current, phototransistor turns off. R4 (1k) pulls RX to ground. Result: RX = LOW.
Signal polarity is preserved. The emitter-follower does not invert. Bus HIGH maps to RX HIGH, bus LOW maps to RX LOW. No software inversion needed on the RX path.
RX Simulation Results
Section titled “RX Simulation Results”Netlist: reference/ibus_rx_path.cir
Test stimulus: byte 0x50 (MFL address) at 9600 baud, 8E1 framing. Bus modeled as 12V source through 100 ohm source impedance.
| Measurement | Value | Threshold | Status |
|---|---|---|---|
| V(RX) HIGH | 3.128V | > 2.475V (ESP32 VIH) | Pass |
| V(RX) LOW | ~0V | < 0.825V (ESP32 VIL) | Pass |
| LED current (peak) | 5.21mA | — | — |
| Rise time (10-90%) | 5.8us | << 104.17us bit time | Pass |
The 5.8us rise time consumes 5.6% of the 104.17us bit period at 9600 baud. Sampling at bit center (52us into the bit) sees a fully settled signal.
TX Path (MCU to Bus via U2 + Q1)
Section titled “TX Path (MCU to Bus via U2 + Q1)”U2’s phototransistor is wired in common-emitter configuration: collector connected to the bus line, emitter to ground. When the phototransistor conducts, it pulls the bus LOW against whatever external pull-up impedance exists on the bus.
Q1 (BC547) drives U2’s LED. The ESP32 TX pin sources current through R5 (470 ohm) and R3 (10k) into Q1’s base.
TX HIGH (UART idle): Q1 base is driven. Q1 saturates (Vce_sat = 0.073V from simulation). Current flows from VCC through R2 (220 ohm) into U2’s LED, through Q1 to ground. I_LED = (3.3V - 1.2V - 0.073V) / 220 = 9.21mA (simulated: 9.71mA). U2’s phototransistor saturates, pulling the bus line toward ground.
TX LOW (transmitting a UART bit): Q1 turns off. No LED current. U2’s phototransistor turns off. The bus is released and returns HIGH via the bus pull-up resistor.
Signal is inverted. TX HIGH produces bus LOW. TX LOW produces bus HIGH. The firmware compensates with uart_set_line_inverse(UART_SIGNAL_TXD_INV) on the ESP32’s UART peripheral, which inverts the TX signal in hardware before it reaches the GPIO pin.
TX Simulation Results
Section titled “TX Simulation Results”Netlist: reference/tx_validated_r2_220.cir
Test conditions: 3.3V VCC, R2=220 ohm, 1k ohm bus pull-up to 12V (worst realistic BMW bus loading). Byte 0x68 (RAD address) at 9600 baud.
| Measurement | Value | Notes |
|---|---|---|
| V(IBUS) LOW | 0.266V | U2 pulls bus to near ground |
| V(IBUS) HIGH | 8.35V | Bus sags from 12V due to U1’s RX LED load (5.4mA through R1) |
| V(IBUS) swing | 8.08V | 0.27V to 8.35V |
| U2 LED current | 9.71mA | Through R2 (220 ohm) when Q1 is on |
| Q1 Vce(sat) | 0.073V | Fully saturated |
| Bus rise time (10-90%) | 9.1us | 8.7% of 104.17us bit time |
| RX loopback HIGH | ~3.1V | U1 sees its own TX through the bus |
| RX loopback LOW | ~0V | Confirms end-to-end signal path |
The 9.1us bus rise time is passive — set by the RC time constant of the bus pull-up and parasitic capacitance. On a real BMW bus, the TH3122 transceivers in other modules provide low-impedance active drive. Actual rise times will be similar or faster.
The R2 Fix: 470 ohm to 220 ohm for 3.3V ESP32
Section titled “The R2 Fix: 470 ohm to 220 ohm for 3.3V ESP32”This is the single most important modification from the original muki01 design.
The Problem
Section titled “The Problem”The muki01 schematic specifies R2=470 ohm, designed for 5V Arduino. With a 5V supply, the LED current through U2 is:
I_LED = (5.0V - 1.2V - Vce_sat) / 470 = ~7.9mAAt 3.3V, this drops to:
I_LED = (3.3V - 1.2V - Vce_sat) / 470 = ~4.3mAThe phototransistor’s collector current is limited by CTR * I_LED. At 4.3mA LED drive and worst-case CTR (80% for PC817A), the maximum collector current is only 3.4mA. Against a 1k ohm bus pull-up (12V / 1k = 12mA needed to pull bus to ground), the phototransistor cannot sink enough current. The bus voltage stays well above the LOW threshold of downstream modules.
Three Parameter Sweeps
Section titled “Three Parameter Sweeps”Three .step simulations explored the operating envelope. Each sweep varies one parameter while holding others at worst-case or typical values.
Sweep 1: R2 Value (Worst-Case CTR, 4.7k Bus)
Section titled “Sweep 1: R2 Value (Worst-Case CTR, 4.7k Bus)”Netlist: reference/tx_sweep_r2.cir
Conditions: Igain=0.5m (PC817A minimum CTR, ~50%), R_PULL=4.7k ohm.
Stepped: R2 = 100, 150, 220, 270, 330, 390, 470 ohm.
| R2 (ohm) | V(IBUS) LOW | LED Current | Status |
|---|---|---|---|
| 100 | 0.128V | 13.0mA | Pass |
| 150 | 0.147V | 9.4mA | Pass |
| 220 | 0.176V | 7.97mA | Pass |
| 270 | 0.189V | 5.91mA | Pass |
| 330 | 0.211V | 5.59mA | Pass |
| 390 | 0.514V | 4.66mA | Marginal |
| 470 | ~0.7V+ | 4.26mA | Fail |
At worst-case CTR, R2=330 ohm is the threshold. R2=390 ohm and above cannot pull the bus low enough. R2=220 ohm provides margin.
Sweep 2: Bus Impedance (R2=470, Typical CTR)
Section titled “Sweep 2: Bus Impedance (R2=470, Typical CTR)”Netlist: reference/tx_sweep_rpull.cir
Conditions: R2=470 ohm (original), Igain=1m (PC817A typical CTR, ~100%).
Stepped: R_PULL = 510, 1000, 2200, 3300, 4700, 6800, 10000 ohm.
| R_PULL (ohm) | V(IBUS) LOW | Status |
|---|---|---|
| 510 | 4.58V | Fail |
| 1000 | 0.268V | Pass |
| 2200 | 0.123V | Pass |
| 3300 | 0.099V | Pass |
| 4700 | 0.082V | Pass |
| 6800 | 0.070V | Pass |
| 10000 | 0.062V | Pass |
With the original R2=470 ohm, the design fails at 510 ohm bus impedance even with typical (not worst-case) CTR. A loaded BMW bus can present impedances in the low kilohm range.
Sweep 3: CTR Grade (R2=470, 1k Bus)
Section titled “Sweep 3: CTR Grade (R2=470, 1k Bus)”Netlist: reference/tx_sweep_ctr.cir
Conditions: R2=470 ohm (original), R_PULL=1k ohm (loaded bus).
Stepped: Igain = 0.3m, 0.5m, 0.8m, 1.0m, 1.5m, 2.3m, 3.4m (mapping to PC817 grades A through D).
| Igain | Approx. Grade | V(IBUS) LOW | Status |
|---|---|---|---|
| 0.3m | Below A | 6.89V | Fail |
| 0.5m | A (min) | 4.41V | Fail |
| 0.8m | A (typ) | 2.51V | Marginal |
| 1.0m | A (max) / B (min) | 0.18V | Pass |
| 1.5m | B (typ) | 0.10V | Pass |
| 2.3m | C | 0.07V | Pass |
| 3.4m | D | 0.05V | Pass |
With R2=470 ohm against a 1k bus, even a typical PC817A (Igain=0.8m) only manages V_LOW=2.51V — marginal at best. You’d need to cherry-pick PC817B or better parts.
The Fix
Section titled “The Fix”R2=220 ohm. This doubles the LED current from ~4.3mA to ~9.7mA. The increased drive current produces roughly double the phototransistor base current, significantly extending the collector current capability.
Validated in reference/tx_validated_r2_220.cir against worst-case realistic conditions (1k bus pull-up, PC817A typical CTR):
- V(IBUS) LOW = 0.266V (well below any logic threshold)
- LED current = 9.71mA (19% of the PC817’s 50mA absolute max rating)
- Supports bus pull-ups down to approximately 530 ohm
No other component changes required.
K-Line Compatibility Analysis (510 ohm Pull-Up)
Section titled “K-Line Compatibility Analysis (510 ohm Pull-Up)”The optocoupler circuit was also evaluated for potential use on OBD-II K-line (a sibling project that uses a transistor interface). K-line has a 510 ohm pull-up to 12V per ISO 9141, requiring the transmitter to sink approximately 23.5mA.
Two additional netlists tested this scenario:
R2 sweep at 510 ohm pull-up (reference/opto_vs_kline_510.cir):
Stepped R2 from 47 to 470 ohm at 10400 baud (K-line rate), typical CTR (Igain=1m).
At R2=220 ohm (the BMW fix value), the phototransistor maxes out at approximately 22.4mA of collector current — right at the edge of the 23.5mA needed. The bus voltage sits around 0.5-1V, which may or may not be interpreted as LOW depending on the ECU’s input threshold.
Only R2=100 ohm (LED current ~20.5mA) reliably pulls the bus below 0.3V at 510 ohm.
CTR grade sweep at 510 ohm, R2=100 (reference/opto_vs_kline_ctr.cir):
Even with R2=100 ohm, worst-case PC817A (Igain=0.5m) fails at 510 ohm bus impedance. Typical CTR and above passes.
Conclusion: The optocoupler circuit is marginal for K-line’s 510 ohm pull-up. A direct transistor interface (BC547 or MOSFET open-drain) is the better choice for OBD-II K-line — it does not have the CTR limitation and can sink arbitrary current up to its collector rating. The optocoupler design is correct for BMW I/K-Bus, where bus impedances are in the kilohm range.
Design Notes
Section titled “Design Notes”1. Bus Rise Time Depends on External Impedance
Section titled “1. Bus Rise Time Depends on External Impedance”The optocoupler’s phototransistor is an open-collector (common-emitter) output. It can actively pull the bus LOW, but the return to HIGH is passive — set by the bus pull-up resistor and parasitic capacitance. The simulated 9.1us rise time (with 1k pull-up, 100pF) is 8.7% of the 104.17us bit time at 9600 baud.
On a real BMW bus, other modules have TH3122 transceivers with active push-pull outputs. These provide low-impedance drive in both directions. The actual rise time on the vehicle bus will be similar to or faster than the simulation.
2. Bench Testing Requires a Low-Impedance 12V Source
Section titled “2. Bench Testing Requires a Low-Impedance 12V Source”U1’s RX LED draws 5.4mA continuously through R1 whenever the bus is at 12V (which is always during idle). If the bench test setup uses only a passive pull-up resistor (e.g., 4.7k to 12V), the bus voltage sags significantly under this load:
- 4.7k pull-up: V_bus drops to ~4.3V (most of the current goes through R1)
- 1k pull-up: V_bus drops to ~8.4V
Use a 12V power supply through 100-510 ohm to simulate a real bus source impedance during development. Or better, a dedicated 12V rail with a series resistor matching the expected bus impedance.
3. PC817 Grade Selection
Section titled “3. PC817 Grade Selection”PC817A (CTR 80-160%) is the cheapest and most widely available grade. With R2=220 ohm, it works for BMW I/K-Bus across the full range of realistic bus impedances. PC817B (CTR 130-260%) or higher grades provide additional margin with no circuit changes — drop-in replacement, same pinout, same package.
The sweep netlists (reference/tx_sweep_*.cir) document the exact operating envelope for each grade.
4. Parasitic Drain
Section titled “4. Parasitic Drain”The interface draws approximately 5.4mA continuously from the bus 12V through R1. This is the U1 RX LED current, always on when the bus is at idle voltage. U2’s LED only draws current during TX (9.7mA pulses, duty cycle dependent on message frequency).
For a vehicle parked long-term, 5.4mA from a ~60Ah battery is roughly 460 days to full discharge (in theory). In practice, the vehicle’s own parasitic drain is much higher. Still, for multi-week parking, either disconnect the interface or add a sleep circuit (MOSFET switch on R1, controlled by a GPIO or the vehicle’s terminal 15 signal).
5. PC817 SPICE Model
Section titled “5. PC817 SPICE Model”All simulations use the PC817 subcircuit inlined from LTspice’s library (lib/sub/PC817.sub). The model uses a voltage-controlled current source (VCCS, G1) with a gain parameter {Igain} to represent the optical coupling. Pin order: 1=Anode, 2=Cathode, 3=Collector, 4=Emitter.
The Igain parameter maps to CTR as follows:
Igain=0.5m— PC817A minimum (~50% effective CTR in the model)Igain=1m— PC817A typical (~100%)Igain=1.5m— PC817B typical (~150%)Igain=2.3m— PC817C typical (~230%)Igain=3.4m— PC817D typical (~340%)
These are approximate. The SPICE model includes a phototransistor (NPN with Bf=1200) driven by the VCCS, so the actual transfer ratio is nonlinear at high drive levels.
SPICE Netlist Inventory
Section titled “SPICE Netlist Inventory”All files in reference/. Simulations target 9600 baud (BMW I/K-Bus) unless noted.
| File | Description |
|---|---|
ibus_rx_path.cir | RX path: bus byte 0x50 at 9600 baud through U1 (PC817) to ESP32 RX. Measures V(RX) levels, LED current, rise/fall times. 3.3V VCC, 100 ohm bus source impedance. |
ibus_tx_path.cir | TX path: ESP32 TX through Q1 (BC547B) and U2 (PC817) to bus. Alternating pulses, 4.7k bus pull-up. Includes U1 RX loopback. R2=220 ohm, Igain=1m. |
tx_validated_r2_220.cir | TX validation: R2=220 ohm fix against worst realistic loading (1k bus pull-up). Byte 0x68 at 9600 baud. Measures bus swing, rise/fall times, Q1 Vce_sat, RX loopback. The primary validation netlist for the R2 fix. |
tx_sweep_r2.cir | Parameter sweep: R2 from 100 to 470 ohm. Worst-case CTR (Igain=0.5m), 4.7k bus pull-up. Single TX pulse. Identifies R2=330 ohm as the pass/fail threshold. |
tx_sweep_rpull.cir | Parameter sweep: bus pull-up from 510 to 10k ohm. R2=470 ohm (original), typical CTR (Igain=1m). Shows failure at 510 ohm, pass at 1k and above. Demonstrates why R2=470 is insufficient for loaded buses. |
tx_sweep_ctr.cir | Parameter sweep: CTR grade (Igain 0.3m to 3.4m). R2=470 ohm, 1k bus pull-up. Shows PC817A (typical) is marginal, PC817B+ passes. Motivated the R2 reduction as an alternative to grade selection. |
opto_vs_kline_510.cir | K-line compatibility: R2 sweep (47 to 470 ohm) against 510 ohm pull-up at 10400 baud. Typical CTR. Shows R2=220 ohm is marginal, R2=100 ohm is the minimum for K-line. |
opto_vs_kline_ctr.cir | K-line compatibility: CTR grade sweep at 510 ohm pull-up, R2=100 ohm, 10400 baud. Shows worst-case PC817A fails even with aggressive R2. Confirms transistor circuit is preferred for K-line. |
Waveform plots (SVG, from initial simulations):
| File | Description |
|---|---|
rx_path_vibus.svg | Bus voltage waveform during RX simulation |
rx_path_vrx.svg | ESP32 RX pin voltage during RX simulation |
tx_path_vibus.svg | Bus voltage waveform during TX simulation |
tx_path_vrx_loopback.svg | RX loopback voltage during TX simulation |
Attribution
Section titled “Attribution”Circuit design from muki01/I-K_Bus (MIT license). R2 modification, SPICE validation, and 3.3V analysis are original work for this project.