Community project

Fail-Safe Engine Vent Lift

ESP32
Photo of Fail-Safe Engine Vent Lift
Generated with AI

Jason Quail

Published August 17, 2026

This fail-safe engine vent lift automatically opens an external vent flap when cylinder-head temperature exceeds 180°C, then closes it again below 150°C. The system uses a K-type thermocouple probe mounted on the cylinder head, an ESP32 microcontroller to monitor temperature and control a 12 V linear actuator, and a watchdog relay that forces the vent open if the controller loses power or crashes.

The guide provides a complete wiring diagram, full parts list with sourcing, and step-by-step assembly instructions including how to wire the thermocouple amplifier, motor driver, logic-level converter, and fused power distribution. Firmware is included with conservative temperature thresholds and safety timeouts. Before road use, the guide walks through proving the fail-open mechanism and verifying the watchdog relay triggers correctly.

Wiring diagram

Wiring diagram for Fail-Safe Engine Vent Lift

Gather all the parts

QtyComponent
1

MAX31855 Thermocouple Amplifier

3.3 V breakout

Cold-junction compensated K-type thermocouple-to-digital converter with SPI interface. Reads thermocouple temperatures up to +1350°C and performs on-chip cold-junction compensation. 3.3V compatible on the Adafruit breakout (includes onboard 3.3V regulator and level shifting). 14-bit thermocouple resolution (~0.25°C). Read-only SPI (SCK, CS, SO); no MOSI needed.

1

K-type Spark-Plug Ring Thermocouple

M14 spark-plug ring, K type

A heat-resistant ring probe fitted beneath a cylinder-head spark plug to measure head temperature.

1

IBT-2 BTS7960 43A High-Current H-Bridge Motor Driver

12 V H-bridge

IBT-2 high-current H-bridge motor driver module built around BTS7960 half-bridge devices. Common modules are sold for 6V-27V motor supplies and 5V logic, with 43A as a module headline rating; practical continuous current depends on board cooling and wiring. Direction and speed are controlled with RPWM/LPWM PWM inputs plus R_EN/L_EN enable lines. Logic-side VCC is separate from the high-power motor supply, and grounds must be common.

1

4-Channel 3.3 V to 5 V Logic-Level Converter

4 channel, 3.3 V/5 V

A small board that raises three ESP32 signals to the 5 V level required by the motor driver and watchdog module.

1

12 V Back-Drivable Linear Actuator with Internal End Stops

12 V, 100 mm stroke, IP65 or better, back-drivable/spring-return compatible, internal end stops

A weather-resistant non-self-locking 12 V actuator whose shaft can be pulled back by the vent-open spring when electrical power is absent.

1

Stainless Steel Vent-Open Return Spring

stainless extension spring, sized to fully open vent

A mechanical spring that pulls the vent open when the controller, motor wiring, or vehicle power fails.

1

15 A Inline Automotive Blade Fuse Holder

15 A blade fuse; size to actuator's specified peak current

A fuse holder placed close to the vehicle's positive supply to protect the actuator wiring from a short circuit.

1

Automotive Protected 12 V-to-5 V DC/DC Converter

9–36 V input, 5 V 3 A output, reverse-polarity and load-dump protected

A sealed automotive power converter that turns vehicle voltage into stable 5 V for the ESP32 and motor-driver logic.

1

External Engine Vent Flap and Hinge

fabricated louvered flap, positive open stop

A fabricated weather-resistant flap and hinge that opens to let cooling air reach the rear-mounted engine.

1

5 V Retriggerable Watchdog Relay Module

5 V retriggerable watchdog, normally-open relay, 0.5–2 s timeout

A timer relay module that powers the actuator driver only while it receives a regular heartbeat from the controller.

Assemble it in 6 steps

1. Make the vent fail open without electricity

Build the vent flap with a solid hinge and a physical open stop. Bolt the fixed end of vent_actuator to a reinforced body bracket and pin its moving clevis to the vent_flap arm. Hook open_return_spring between the flap arm and a fixed body bracket so it pulls the flap all the way to its open stop whenever the actuator is not holding it closed.

  • Before adding any wiring, disconnect the actuator and pull the flap closed by hand. Let go: the spring must open the flap fully every time.
  • Choose a back-drivable actuator or an actuator specifically specified by its maker as compatible with the return spring. A typical self-locking actuator will defeat this safety feature.
  • Do not use a spring that merely helps the flap move; it must reliably open the flap against dirt, vibration, and airflow. A vent that remains closed after a power failure can overheat the engine.
  • Keep the flap outside the exhaust stream and clear of hot exhaust parts, moving belts, and the engine lid.

2. Fit the cylinder-head temperature probe

With the engine completely cold, remove the spark plug from the cylinder head you want to monitor and place the ring of cht_probe beneath that plug. Refit and torque the spark plug to the engine manufacturer’s specification. Route the two probe wires through heat sleeve and secure them away from plug leads, the fan belt, exhaust, and sharp sheet metal.

  • Keep the thermocouple extension wire as a matched pair all the way to the sensor board; do not splice ordinary copper wire into either lead.
  • Put the MAX31855 board in the sealed electronics enclosure, not in the hot engine bay.
  • Do not work on the spark plug or head with a hot engine — the plug and surrounding metal can burn you.
  • Keep the thermocouple wires away from ignition leads because ignition noise can corrupt temperature readings.

3. Mount the protected electronics

Mount the ESP32, cht_interface, logic_shifter, watchdog_relay, and auto_buck inside a gasketed, vibration-isolated enclosure in a cool, dry location. Mount motor_driver on a metal heat-spreading plate inside a protected location, with its heavy motor wires kept separate from the small sensor wires.

  • Use crimp terminals, strain relief, and automotive-rated wire. Label both ends before closing the enclosure.
  • Bring the chassis ground return to a clean bare-metal body point with a star washer, then protect that point from corrosion.
  • Do not mount the ESP32 or the unsealed driver board where engine heat, road spray, or fuel vapour can reach it.
  • Do not share thin sensor wiring with the actuator current path; a loose high-current connection can overheat.

4. Wire the temperature reader and control signals

Connect cht_probe positive lead to cht_interface T+ (temperature signal), and negative lead to cht_interface T- (temperature signal). Wire cht_interface VCC to ESP32 3V3 (power), GND to ESP32 GND (ground), SCK to GPIO18 (clock), SO to GPIO19 (data), and CS to GPIO4 (select). Wire logic_shifter LV to 3V3 (power), HV to 5V (power), and GND to GND (ground). Wire LV1 to GPIO25 (close signal), LV2 to GPIO26 (open signal), and LV3 to GPIO27 (watchdog heartbeat). Connect HV1 to motor_driver RPWM (close command), HV2 to motor_driver LPWM (open command), and HV3 to watchdog_relay TRIG (heartbeat).

  • All low-voltage modules need the same ground connection or their signals will not have a reliable reference.
  • Use twisted pair or shielded cable for the thermocouple run; connect any cable shield at the enclosure end only.
  • Make sure the MAX31855 board receives 3.3 V, not 5 V — swapped or excessive power can damage it.
  • GPIO4 is used as a flexible select signal rather than a fixed SPI pin; this is normal on this ESP32, but check the first temperature reading during bench testing.

5. Wire the fused vehicle and actuator power

From an ignition-switched 12 V feed, wire the battery side to power_fuse Battery in (power). Wire power_fuse Protected out to auto_buck VIN+ (power) and watchdog_relay COM (power). Wire auto_buck VIN- to chassis ground (ground), 5V OUT to the ESP32 VIN pin, motor_driver VCC, motor_driver R_EN, motor_driver L_EN, logic_shifter HV, and watchdog_relay VCC (power). Connect every listed low-voltage ground to the same chassis-ground return (ground). Wire watchdog_relay NO to motor_driver B+ (actuator power), motor_driver B- to chassis ground (ground), M+ to vent_actuator Motor lead A (motor), and M- to vent_actuator Motor lead B (motor).

  • Place the inline fuse as close as practical to the point where the new 12 V feed is taken.
  • Use wire gauge and a fuse value based on the actuator manufacturer’s measured peak current; 15 A is only the starting design value for an actuator drawing under 5 A.
  • Disconnect the vehicle battery while installing the new 12 V feed. An unfused wire can start a fire if it rubs through on metal.
  • Do not power the actuator from the ESP32 or its small USB connector. The actuator needs its own fused 12 V path.

6. Prove the safety action before road use

With the vehicle safely parked and the engine off, power the system. Confirm the vent first moves toward open. Then remove the fuse or unplug the controller: watchdog_relay must drop out and open_return_spring must pull vent_flap fully open. Reconnect power and use a controlled warm-air test at the thermocouple ring only after the mechanism has passed the no-power test.

  • If the first powered movement goes the wrong way, disconnect vehicle power and swap the two actuator wires at motor_driver M+ and M-.
  • Recheck the vent after driving on a rough road; vibration can loosen brackets and clevis pins.
  • Never test this system by deliberately overheating the engine. Use the sensor reading and a safe bench heat source first.
  • Do not drive the car until removing electrical power reliably leaves the vent fully open.

Review all connections

1. Connections between "cht_probe" and "ESP32"

Functioncht_probeESP32
analogPositive leadMAX31855 Thermocouple Amplifier T+EXT
analogNegative leadMAX31855 Thermocouple Amplifier T-EXT

2. Connections between "cht_interface" and "ESP32"

Functioncht_interfaceESP32
powerVCC3V3
groundGNDGND
spiSCKGPIO 18
spiCSGPIO 4
spiSOGPIO 19

3. Connections between "motor_driver" and "ESP32"

Functionmotor_driverESP32
powerVCC5V
groundGNDGND
pwmRPWM4-Channel 3.3 V to 5 V Logic-Level Converter HV1EXT
pwmLPWM4-Channel 3.3 V to 5 V Logic-Level Converter HV2EXT
powerR_EN5V
powerL_EN5V
powerB+5 V Retriggerable Watchdog Relay Module NOEXT
groundB-GND
dataM+12 V Back-Drivable Linear Actuator with Internal End Stops Motor lead AEXT
dataM-12 V Back-Drivable Linear Actuator with Internal End Stops Motor lead BEXT

4. Connections between "logic_shifter" and "ESP32"

Functionlogic_shifterESP32
powerLV3V3
powerHV5V
groundGNDGND
pwmLV1GPIO 25
pwmLV2GPIO 26
digitalLV3GPIO 27

5. Connections between "vent_actuator" and "ESP32"

Functionvent_actuatorESP32
dataVent clevisExternal Engine Vent Flap and Hinge Actuator armEXT
dataBody clevisreinforced fixed body bracketEXT

6. Connections between "open_return_spring" and "ESP32"

Functionopen_return_springESP32
dataVent endExternal Engine Vent Flap and Hinge Spring hookEXT
dataFixed endreinforced fixed body bracketEXT

7. Connections between "power_fuse" and "ESP32"

Functionpower_fuseESP32
powerBattery inbattery positive through an ignition-switched 12 V feedEXT
powerProtected outAutomotive Protected 12 V-to-5 V DC/DC Converter VIN+EXT

8. Connections between "auto_buck" and "ESP32"

Functionauto_buckESP32
groundVIN-GND
power5V OUT5V
groundGND OUTGND

9. Connections between "vent_flap" and "ESP32"

Functionvent_flapESP32
dataHinge bracketreinforced vehicle-body hinge bracketEXT

10. Connections between "watchdog_relay" and "ESP32"

Functionwatchdog_relayESP32
powerVCC5V
groundGNDGND
digitalTRIG4-Channel 3.3 V to 5 V Logic-Level Converter HV3EXT
powerCOM15 A Inline Automotive Blade Fuse Holder Protected outEXT

Deploy the firmware

#include <Arduino.h>
#include <Adafruit_MAX31855.h>
#include <math.h>

// Matches the actual wires in the project.

// Hoisted type definitions
enum VentState { OPEN, CLOSED, MOVING_OPEN, MOVING_CLOSED };


// Forward declarations
void stopActuator();
void startOpening();
void startClosing();
void finishMotionIfDue();
void sendHeartbeat();
void requestSafeOpen();
void evaluateTemperature();

constexpr int THERMO_SCK_PIN = 18;
constexpr int THERMO_CS_PIN = 4;
constexpr int THERMO_SO_PIN = 19;
constexpr int CLOSE_PWM_PIN = 25;
constexpr int OPEN_PWM_PIN = 26;
constexpr int HEARTBEAT_PIN = 27;

// Conservative cylinder-head-temperature limits for the external auxiliary vent.
constexpr float OPEN_AT_C = 180.0F;
constexpr float CLOSE_BELOW_C = 150.0F;
constexpr unsigned long SAMPLE_INTERVAL_MS = 1000;
constexpr unsigned long HEARTBEAT_INTERVAL_MS = 100;
// 100 mm at 5 mm/s takes 20 seconds; this adds a small margin for linkage travel.
constexpr unsigned long FULL_TRAVEL_MS = 22000;
constexpr unsigned long STARTUP_OPEN_MS = 5000;
constexpr uint8_t DRIVE_PWM = 255;

Adafruit_MAX31855 thermocouple(THERMO_SCK_PIN, THERMO_CS_PIN, THERMO_SO_PIN);


VentState ventState = OPEN;
unsigned long motionStartedAt = 0;
unsigned long lastSampleAt = 0;
unsigned long lastHeartbeatAt = 0;
unsigned long startupAt = 0;
bool startupOpening = true;

void stopActuator() {
  analogWrite(CLOSE_PWM_PIN, 0);
  analogWrite(OPEN_PWM_PIN, 0);
}

void startOpening() {
  // If this moves the flap closed during the first bench test, swap the two motor leads at M+ and M-.
  analogWrite(CLOSE_PWM_PIN, 0);
  analogWrite(OPEN_PWM_PIN, DRIVE_PWM);
  ventState = MOVING_OPEN;
  motionStartedAt = millis();
}

void startClosing() {
  analogWrite(OPEN_PWM_PIN, 0);
  analogWrite(CLOSE_PWM_PIN, DRIVE_PWM);
  ventState = MOVING_CLOSED;
  motionStartedAt = millis();
}

void finishMotionIfDue() {
  if ((ventState == MOVING_OPEN || ventState == MOVING_CLOSED) &&
      millis() - motionStartedAt >= FULL_TRAVEL_MS) {
    bool wasOpening = (ventState == MOVING_OPEN);
    stopActuator();
    ventState = wasOpening ? OPEN : CLOSED;
  }
}

void sendHeartbeat() {
  // The external watchdog must see regular transitions; loss of this signal removes actuator power.
  if (millis() - lastHeartbeatAt >= HEARTBEAT_INTERVAL_MS) {
    digitalWrite(HEARTBEAT_PIN, !digitalRead(HEARTBEAT_PIN));
    lastHeartbeatAt = millis();
  }
}

void requestSafeOpen() {
  if (ventState != OPEN && ventState != MOVING_OPEN) {
    startOpening();
  }
}

void evaluateTemperature() {
  double reading = thermocouple.readCelsius();
  if (isnan(reading)) {
    // A disconnected, damaged, or unreadable head sensor must never leave the vent closed.
    requestSafeOpen();
    return;
  }

  float headC = static_cast<float>(reading);
  if (headC >= OPEN_AT_C) {
    requestSafeOpen();
  } else if (headC <= CLOSE_BELOW_C && ventState != CLOSED && ventState != MOVING_CLOSED) {
    startClosing();
  }
  // Between 150 C and 180 C, leave the vent where it is. This prevents repeated cycling.
}

void setup() {
  pinMode(CLOSE_PWM_PIN, OUTPUT);
  pinMode(OPEN_PWM_PIN, OUTPUT);
  pinMode(HEARTBEAT_PIN, OUTPUT);
  stopActuator();
  digitalWrite(HEARTBEAT_PIN, LOW);
  thermocouple.begin();
  startupAt = millis();

  // Begin by trying to open. The spring is the real fail-safe if this electronics path has failed.
  startOpening();
}

void loop() {
  sendHeartbeat();
  finishMotionIfDue();

  if (startupOpening) {
    if (millis() - startupAt >= STARTUP_OPEN_MS) {
      stopActuator();
      ventState = OPEN;
      startupOpening = false;
    }
    return;
  }

  if (millis() - lastSampleAt >= SAMPLE_INTERVAL_MS) {
    lastSampleAt = millis();
    evaluateTemperature();
  }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik