Ilya Shinkeyev
Back to Home Projects · Part of the Campervan Build
Embedded Systems · Deep Dive

Diesel Heater Controller

Reverse-engineering a 12V diesel heater's wireless remote and rebuilding its control system from scratch — a multi-year evolution from a hot-glued servo rig to an ESP32 that replays the heater's own 433 MHz radio and hosts a phone Web UI.

3D Model

Loading 3D model…
Drag · right-drag pan · scroll zoom
Diesel Heater RF Remote — Full Assembly (V5)

Drag to rotate · scroll to zoom · ⤢ Expand for cross-section, explode & isolate.

The story, version by version

It started as a cheap diesel heater off Amazon and turned into a multi-year obsession with controlling it well. Each version fixed the previous one's biggest annoyance — from a servo literally slapping a button, to an ESP32 that speaks the heater's own radio language.

  1. V0 2020 · the spark

    Bought a cheap diesel heater off Amazon and bench-tested the whole system before trusting it in the van. No custom electronics yet — just proving the heater worked and getting a feel for how its wireless remote behaved. I had no idea it would grow into a years-long project.

  2. V1 2020–2022 · servos

    The first automation. An Arduino read cabin temperature from a DS18B20 sensor, showed it on an OLED, and drove two hobby servos hot-glued over the stock remote to physically press ON and OFF — holding a 68–74°F window on its own. Charming, but the servos drifted and the glue mounts rattled loose with the van's vibration. It lived in a wood-and-plexiglass box, spray-painted Space Blue.

  3. V2 2023 · relay, on the breadboard

    Ditched the servos. I soldered wires straight onto the remote's PCB and let the Arduino pulse a relay across the ON/OFF button — an electrical "press" instead of a mechanical one. Far more reliable, and proven out first on a breadboard.

  4. V3 2025 · relay, made permanent

    The same idea, cleaned up and made real: a proper enclosure, tidier wiring, and a refactor of the code into reusable helpers with a fail-safe that shows an error rather than acting on a dead temperature sensor.

  5. V4 2025 · ESP32 + RF · field-tested

    The big leap. Using an RF receiver and a logic analyzer, I sniffed and decoded the remote's 433 MHz packets, then replayed them from an ESP32 through a SYN115 transmitter — removing the physical remote from the loop entirely. It adds a phone Web UI over the ESP32's own WiFi, a 4-page OLED, anti–short-cycle protection, and a boots-disarmed safety default. It ran in the van, fact-checked against a thermometer — and became the base for V5.

  6. V5 2026 · the build running now

    The "holy grail." A reliability/hardening pass on V4 — same hardware, same RF codes — built to be trusted unattended: closed-loop RF re-assertion (a dropped 433 MHz burst self-heals on the next pass), a fully non-blocking loop (temperature and RF on millis() timers, no delay()), and a watchdog that reboots to a safe DISARMED state. Plus a redesigned, fully self-contained web UI (temperature gauge + live sparkline, no external CDN) and an OLED Wi-Fi QR code to hop onto the heater's network. This is what runs today.

Code Progression

How the firmware actually evolved — each tab opens on the line that makes that version what it is; hit Show full code to load the complete sketch. V5 is the latest — a hardened, self-healing pass on V4.

v5_diesel_heater.ino · ESP32 + 433 MHz RF · the hardened build running now
// V5 — the hardened "holy grail". Same hardware & 433 MHz codes as V4; a reliability pass, not a rewrite.
#include <RCSwitch.h>
#include "esp_task_wdt.h"          // watchdog: reset a wedged loop, reboot DISARMED (heater off)

const unsigned long CODE_ON = 1931481520UL, CODE_OFF = 1931478280UL;   // captured from the stock remote

// Non-blocking RF: send ONE frame per loop tick on a millis() timer — never delay() the control loop.
void rfService(unsigned long now) {
  if (!rfJob.active || (long)(now - rfJob.nextAt) < 0) return;
  rc.send(rfJob.code, CODE_BITS);
  if (--rfJob.framesLeft == 0) rfJob.active = false;
  else rfJob.nextAt = now + rfGapMs;
}

// Closed-loop RF re-assertion — 433 MHz is one-way and lossy, so re-send the desired state
// every few minutes; a dropped burst simply self-heals on the next pass.
if (armed && !sensorFault && now - lastReassertMs >= RF_REASSERT_MS) {
  rfEnqueue(lastHeaterState == H_ON ? CODE_ON : CODE_OFF);
}

// Plus non-blocking DS18B20, deadband + anti-short-cycle, an OLED Wi-Fi QR to join the AP,
// and a self-contained web UI (temp gauge + live sparkline) — no external CDN.

How It Works

The stock remote communicated with the heater over a one-way 433MHz link. Using an RF receiver module and a logic analyzer, I captured and decoded the button packets, then replayed them from an ESP32 — removing the physical remote from the loop entirely.

The ESP32 hosts a small Web UI on the van's local network, so the heater can be controlled from a phone: power, setpoint temperature, and a simple thermostat loop that holds the cabin temperature overnight. More detail, schematics, and the full source will be added here.

Bill of materials

Cost of the electronics and printed parts the build actually used — multi-pack items are prorated from their pack price. Item names link to what I bought.

Item Used Cost
ELEGOO PLA Filament (custom printed parts)
$14.39 / kg
~175 g$2.52
M3 Screw Assortment Kit
$14.99 / 960-pc kit
26$0.41
M3 Brass Heat-Set Inserts
$9.99 / 300-pack
26$0.87
ELEGOO 0.96″ OLED Display
$9.99 / 3-pack
1$3.33
HiLetgo ESP-WROOM-32 Dev Board
$9.99 each
1$9.99
SYN115 433 MHz RF Transmitter
$9.99 / 5 TX+RX sets
1$2.00
DS18B20 Temp Sensor + Breakout
$8.99
1$8.99
USB-C to USB-A Cable
$8.97 / 5-pack
1$1.79
90° Male Header Strips (± bus)
$7.99 / 400 pins
16$0.32
Jumper Wires (Dupont)
$6.98 / 120-pack
12$0.70
Total materials used$30.92

The diesel heater itself, plus common shop supplies (solder, breadboard, tools), were on-hand and left off the tally.