GitHub : Carputer-project/esp32_body_controller
TCP command server for body control — HVAC relays, door locks, windows, fan speed, remote start, and Kenwood radio I2C sniffer. Runs on Adafruit Feather ESP32.
Creates the Carputer_ECU WiFi access point (192.168.4.1) and listens for TCP commands on port 5000. Controls all body electronics via ULN2003A relay drivers. Sniffs the Kenwood radio's I2C bus to capture volume knob changes.
Parameter Value WiFi Mode AP (Access Point) AP SSID Carputer_ECU AP Password 12345678 AP IP / Gateway 192.168.4.1 Subnet 255.255.255.0 TCP server port 5000 (commands from carputer app) Mesh UDP port 5010 (V2V mesh broadcast) Status push interval 2000ms
Relay Wiring (via ULN2003A Darlington Arrays) Two ULN2003A driver ICs handle all relay switching. ULN2003A provides open-collector outputs with built-in flyback diodes for inductive loads.
ULN2003A #1 — Body Relays ESP32 GPIO Function Relay Wire To Notes GPIO 25 Door Lock LOCK relay Coil+ to ULN2003A #1 pin 1 HIGH = locked GPIO 26 Window Up WIN_UP relay Coil+ to ULN2003A #1 pin 2 Pulse 500ms HIGH then LOW GPIO 27 Window Down WIN_DN relay Coil+ to ULN2003A #1 pin 3 Pulse 500ms HIGH then LOW GPIO 32 Extra 1 EXTRA1 relay Coil+ to ULN2003A #1 pin 4 Generic (interior light bridge) GPIO 33 Extra 2 EXTRA2 relay Coil+ to ULN2003A #1 pin 5 Door chime buzzer (pulse 200ms) / Siren
ULN2003A #2 — HVAC & Fan Relays ESP32 GPIO Function Relay Wire To Notes GPIO 2 HVAC Relay HVAC relay Coil+ to ULN2003A #2 pin 1 HIGH = HVAC on GPIO 4 AC Relay AC relay Coil+ to ULN2003A #2 pin 2 HIGH = AC on GPIO 5 Remote Start RSTART relay Coil+ to ULN2003A #2 pin 3 HIGH = remote start active GPIO 18 Fan 1 Relay FAN1 relay Coil+ to ULN2003A #2 pin 4 Speed 1 = HIGH GPIO 12 Fan 2 Relay FAN2 relay Coil+ to ULN2003A #2 pin 5 Speed 2 = HIGH
Fan speed logic:
Speed GPIO 18 (FAN1) GPIO 12 (FAN2) 0 (off) LOW LOW 1 HIGH LOW 2 HIGH HIGH
ULN2003A Pin 1-7 (IN1-IN7) ← ESP32 GPIO (via 1KΩ series resistor recommended)
ULN2003A Pin 8 (GND) → Common GND
ULN2003A Pin 9 (COM) → +12V (relay coil supply)
ULN2003A Pin 10-16 (OUT1-OUT7) → Relay coil negative side
Relay coil positive side → +12V
Kenwood Radio (TEA6320T) — I2C Sniffer ESP32 Pin Wire To Function GPIO 21 TEA6320T SDA (pin 22 on TEA6320T) I2C data — sniffer mode GPIO 19 TEA6320T SCL (pin 21 on TEA6320T) I2C clock — sniffer mode GND Radio chassis GND Common ground
I2C Configuration:
Address: 0x40 (7-bit), 0x80 (8-bit write) Bus speed (hybrid master): 50 kHz Sniffer: GPIO interrupt-driven, monitors MICOM writes to TEA6320T Reg Address Function Range VOLUME 0x00 Volume + Loudness dB+43: -31dB(12) to +20dB(63). Bit 6: LOFF FFR 0x01 Front-Right fader 0-15 (0=off) FFL 0x02 Front-Left fader 0-15 (0=off) FRR 0x03 Rear-Right fader 0-15 (0=off) FRL 0x04 Rear-Left fader 0-15 (0=off) BASS 0x05 Bass 0-31 (16=flat) TREBLE 0x06 Treble 0-31 (16=flat) SWITCH 0x07 Source + Mute [GMU][X][X][X][X][SC2][SC1][SC0]
Source select (SC bits): 0=CD(A), 1=B, 2=AUX(C), 3=D, 4=E, 5=Mono
Audio Source ──[1µF]──┬── TEA6320T pin 13 (L) / pin 20 (R)
│
[47kΩ]
│
GND
ESP32 normally sniffs I2C via GPIO interrupts. When app sends radio command:
Disable ISR Wire.begin(50kHz) — switch to master modeWrite register to TEA6320T (~20ms) Wire.end() — release busReconfigure GPIO as input with pullup Re-enable ISR — resume sniffing Knob Action ESP32 Output Carputer Effect Turn right (vol up) J:R + VOL:<dB>Navigate right + media volume up Turn left (vol down) J:L + VOL:<dB>Navigate left + media volume down Push (select) J:SSelect/enter (requires solder wire to GPIO13)
ESP32 GPIO Function Type Notes GPIO 36 Joystick Y Analog (ADC) <1200=down, >2800=up, 250ms debounce GPIO 39 Joystick X Analog (ADC) <1200=left, >2800=right, 250ms debounce GPIO 22 SELECT button INPUT_PULLUP Active LOW GPIO 23 EXIT button INPUT_PULLUP Active LOW GPIO 13 Radio Knob Push INPUT_PULLUP Active LOW, sends J:S (solder wire required)
For future expansion (5-button joypad, sensors, etc.):
GPIO Type Notes GPIO 14 INPUT_PULLUP Best for buttons GPIO 16 INPUT_PULLUP Best for buttons GPIO 17 INPUT_PULLUP Best for buttons GPIO 34 Input only Needs external 10K pull-up to 3.3V GPIO 35 Input only Needs external 10K pull-up to 3.3V GPIO 0 INPUT_PULLUP Strapping pin (LOW at boot = download mode)
Command Protocol (TCP text) Command Action Pin Used H0/H1HVAC off/on GPIO 2 S0-S2Fan speed GPIO 18 + GPIO 12 A0/A1AC off/on GPIO 4 L0/L1Unlock/lock GPIO 25 W1Window up (500ms pulse) GPIO 26 W0Window down (500ms pulse) GPIO 27 R0/R1Remote start off/on GPIO 5 P10/P11Extra 1 off/on GPIO 32 P20/P21Extra 2 off/on GPIO 33 CChime (200ms pulse) GPIO 33 K0-K4Radio source I2C → TEA6320T V-31..V20Radio volume (dB) I2C → TEA6320T M0/M1Radio unmute/mute I2C → TEA6320T B0-B31Bass (16=flat) I2C → TEA6320T T0-T31Treble (16=flat) I2C → TEA6320T D-15..D15Fader I2C → TEA6320T O0/O1Loudness off/on I2C → TEA6320T
{"cmd" : " bind" , "vin" : " ..." } // Bind module to VIN
{"cmd" : " unbind" } // Unbind module
{"cmd" : " arm" } // Arm alarm
{"cmd" : " disarm" } // Disarm alarm
{"cmd" : " remote_start" } // Remote start engine
{"cmd" : " remote_stop" } // Remote stop engine
{"cmd" : " lock" } // Lock doors
{"cmd" : " unlock" } // Unlock doors H:0 S:0 A:0 L:0 R:0 F:0 P:0,0 K:0 V:0 M:1 B:16 T:16 D:0 O:0
cd esp32_body_controller
pio run # Build
pio run --target upload # Upload via USB
pio device monitor -b 115200 # Serial monitorESP32 Arduino core ArduinoJson — JSON (v7)Wire (I2C, built-in) — TEA6320T communication