Reviving LG fridge with custom PID control

i.e. controlling the Nidec BLDC Fan Motor with ESPHome

Identifying the part

The label reads Nidec Z40E12MS1A0-57K06, BLDC 1A DC12V, made in Vietnam. There’s also an EAU64843803 code, which is an LG appliance part number — a strong hint that no public datasheet exist.

The important detail is that it’s a BLDC with an integrated driver. Flipping the rotor off reveals a small PCB with a driver IC, a few passives, and a silkscreen legend that does all the documentation work for us:

FG
PWM
VBB
GND

Four pads, four wires. No external ESC required — feed it 12 V and a logic signal and it runs.

There’s also a pad marked CW near R2, which is a direction-select input.

Checking it before applying power

Salvaged electronics deserve a multimeter pass first. Measuring against GND:

PinReadingMeaning
VBB~26 kΩLeakage through the driver IC
PWM~9 kΩA real pull-down resistor. The input is biased, not floating.
FGOpenCorrect for an idle open-collector output with no pull-up.

First power-up

Bench supply at 12.0 V with the current limit set to 0.4 A. VBB and GND only, PWM left floating.

Result: 20 mA and no rotation.

That’s not a failure — 20 mA is the driver IC’s quiescent draw. The chip is alive, it just isn’t commutating. Given the 9 kΩ pull-down, a floating PWM input reads as logic low, which means off.

Tying PWM to VBB spun it up immediately (using 1kΩ resistor). Non-inverted logic confirmed: high means run. The CW pad never needed touching, so its default floating state gives a valid direction.

A follow-up test showed 3.3 V is enough to trigger the input, while 12 V is clearly tolerated. That wide range means a microcontroller pin can drive it directly with no level shifting.

Characterising the interface

SignalBehaviour
VBB12 V, ~20 mA idle, 1 A is the stall/load rating
PWMTrue duty-cycle input, ~25 kHz, non-inverted, 3.3 V logic sufficient, floating = off
FGOpen-collector tach, needs a pull-up, typically 3 pulses per revolution (unconfirmed)
CWDirection select, safe to leave unconnected (unconfirmed; not expose over the wires)

PWM here is a genuine square-wave duty input.

Why ESP32 and not ESP8266

The motor wants 25 kHz. esp8266_pwm is a software implementation and gets unreliable above roughly 1 kHz. The ESP32’s LEDC peripheral generates 25 kHz in hardware without touching the CPU.

Here’s a basic esphome script.

ESP32-C3 PinConnects toNotes
GPIO1Fan PWM control input25 kHz PWM, drives fan_pwm output
GPIO2Fan FG (tachometer) outputOpen-collector — internal pull-up enabled in software
GPIO3DS18B20 data (DQ)Needs an external ~4.7kΩ pull-up resistor to 3.3V
3.3VDS18B20 VDD, pull-up resistorNormal (non-parasitic) power mode
GNDDS18B20 GND, fan signal GNDCommon ground with the ESP32
12V supply (external, not ESP32)Fan V+
12V supply GNDFan GNDTie to common ground with ESP32/DS18B20 GND
esphome:
  name: fridge

esp32:
  variant: esp32c3
  framework:
    type: esp-idf

logger:
  level: DEBUG
  hardware_uart: UART0

api:
ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# ---------------------------------------------------------------
# PWM output -> motor PWM pin
# 25 kHz matches the Nidec Z40E12MS1A0 input spec.
# 10-bit resolution is the practical ceiling at this frequency.
# ---------------------------------------------------------------
output:
  - platform: ledc
    id: fan_pwm
    pin: GPIO1
    frequency: 25000Hz
    min_power: 15%
    max_power: 100%
    zero_means_zero: true

sensor:
  - platform: pulse_counter
    pin:
      number: GPIO2
      mode:
        input: true
        pullup: true
    id: fan_tach
    name: "Motor RPM"
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    update_interval: 5s
    count_mode:
      rising_edge: DISABLE
      falling_edge: INCREMENT
    filters:
      - multiply: 0.3333

  - platform: dallas_temp
    id: fridge_temperature
    name: "Fridge Temperature"
    update_interval: 30s

  - platform: pid
    name: "Motor Duty"
    climate_id: fridge_climate
    type: COOL

one_wire:
  - platform: gpio
    pin: GPIO3

climate:
  - platform: pid
    id: fridge_climate
    name: "Fridge"
    sensor: fridge_temperature
    default_target_temperature: 4°C
    cool_output: fan_pwm
    control_parameters:
      kp: 0.04
      ki: 0.0015
      kd: 0.0
    visual:
      min_temperature: -2 °C
      max_temperature: 10 °C
      temperature_step: 0.5 °C