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 mode
Write register to TEA6320T (~20ms)
Wire.end() — release bus
Reconfigure 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:S
Select/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/H1
HVAC off/on
GPIO 2
S0-S2
Fan speed
GPIO 18 + GPIO 12
A0/A1
AC off/on
GPIO 4
L0/L1
Unlock/lock
GPIO 25
W1
Window up (500ms pulse)
GPIO 26
W0
Window down (500ms pulse)
GPIO 27
R0/R1
Remote start off/on
GPIO 5
P10/P11
Extra 1 off/on
GPIO 32
P20/P21
Extra 2 off/on
GPIO 33
C
Chime (200ms pulse)
GPIO 33
K0-K4
Radio source
I2C → TEA6320T
V-31..V20
Radio volume (dB)
I2C → TEA6320T
M0/M1
Radio unmute/mute
I2C → TEA6320T
B0-B31
Bass (16=flat)
I2C → TEA6320T
T0-T31
Treble (16=flat)
I2C → TEA6320T
D-15..D15
Fader
I2C → TEA6320T
O0/O1
Loudness 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 monitor
ESP32 Arduino core
ArduinoJson — JSON (v7)
Wire (I2C, built-in) — TEA6320T communication