Inhaltsverzeichnis
IKEA UPPÅTVIND ESP32-C3 Smart Mod
Overview
This project describes how to modify the IKEA UPPÅTVIND air purifier using an ESP32 and ESPHome for local smart home integration.
The original button and LED/PWM signals of the purifier are connected to the ESP32, allowing full control and status monitoring through Home Assistant.
The modification keeps the original fan controller and hardware intact while adding WiFi connectivity and automation capabilities.
Price of the purifier: 25.05.2025 30€ at the local store and have a good availability.
Motivation
- Manual button control stays
- Integration in Home Assistant
- Automate air purification (3d Printer/ spray paint can/ food dehydrator/ Dust )
- Monitor purifier state remotely
- Reuse existing hardware instead of replacing it
Features
- ESPHome integration
- Home Assistant fan entity
- Fan speed detection
- Simulated hardware button presses
- WiFi control
- Optional automation with sensors i have a Pico Air Monitoring Expansion running so it can be used to trigger the UPPÅTVIND
Cost Estimate
Approximate modification cost:
- ESP32-C3 Super Mini: ~2–5$
- Resistors / wiring: ~1$
- Optional level shifter: ~1$
- Optional soldering material/tools
Total: ~3–8$ excluding the purifier itself.
Hardware plan
Required:
- IKEA UPPÅTVIND Air Purifier
- ESP32-C3 Super Mini
- 10kΩ resistor
- 20kΩ resistor
- Wiring
- Soldering equipment
Optional:
- Logic level shifter
- Heat shrink tubing
- Multimeter
Internal Signals
The purifier PCB exposes several useful test points. Here is a nice Project esphome-ikea-uppatvind
Known signals:
| Test Point | Function |
|---|---|
| TP2 | GND |
| TP3 | 5V |
| TP4 | Button Input |
| PWM | FAN PWM Sense |
| TP6 | Optional LED Signal |
GPIO Mapping
i added a Transistor
BC547C ┌─────┐ │ │ └─────┘ | | | C B E
| UPPÅTVIND | ESP32-C3 | Function |
|---|---|---|
| TP2 | GND | Ground |
| TP3 | 5V | Power |
| TP4 | GPIO10 | Button Simulation |
| PWM | GPIO4 | PWM Detection |
| TP6 (optional) | GPIO5 | Additional LED State |
Wiring
Basic Wiring
UPPÅTVIND
PWM ---10kΩ ---+--- GPIO0 - yellow
|
20kΩ
+-------- GND black
TP6 ---10kΩ ---+--- GPIO1 - green
|
20kΩ
+-------- GND black
TP2 -------------------- GND - black
TP3 -------------------- 5V - red
TP4 -------------------- BC547 C - blue
ESP GPIO04 -- 4.7k ----- BC547 B
TP2 -------------------- BC547 E
System Behavior
- ESP is always powered
- Original purifier MCU stays active
- ESP simulates button presses
- Fan state is detected through LED/PWM signal monitoring
- Home Assistant controls the purifier locally
ESPHome
Functions implemented:
- Button simulation
- Fan speed detection
- Fan entity
- API integration
- WiFi fallback AP
# ------------------------------
# Sensors Configuration
# ------------------------------
# ------------------------------
# Sensors & Status
# ------------------------------
binary_sensor:
- platform: status
name: "${devicename} Status"
# TP6 -> GPIO1 (Filter LED)
- platform: gpio
pin:
number: GPIO1
mode:
input: true
pullup: true
name: "UPPATVIND Filter Warning"
id: filter_led
# ------------------------------
# Information Sensors
# ------------------------------
- platform: pulse_counter
pin:
number: GPIO0
mode:
input: true
pullup: true
name: "UPPATVIND Motor Pulses"
id: uppatvind_rpm
update_interval: 1s
internal: true
# ------------------------------
# Text Sensors (Diagnostics)
# ------------------------------
text_sensor:
- platform: wifi_info
ip_address:
name: "${devicename} IP Address"
ssid:
name: "${devicename} Connected SSID"
bssid:
name: "${devicename} BSSID"
mac_address:
name: "${devicename} MAC Address"
- platform: template
name: UPPATVIND Mode
id: uppatvind_mode
lambda: |-
float rpm = id(uppatvind_rpm).state;
if (rpm < 1000)
return {"Off"};
if (rpm < 9500)
return {"Low"};
if (rpm < 13500)
return {"Medium"};
return {"High"};
update_interval: 1s
# ------------------------------
# Restart Switch Configuration
# ------------------------------
switch:
- platform: restart
name: ESP-UPPATVIND restart
# ------------------------------
# TP4 -> GPIO04 (Button)
# ------------------------------
output:
- platform: gpio
pin:
number: GPIO4
inverted: false
id: fan_button
select:
- platform: template
name: "UPPATVIND Speed"
id: uppatvind_speed
optimistic: false
options: ["Off", "Low", "Medium", "High"]
lambda: |-
return id(uppatvind_mode).state;
set_action:
- if:
condition:
lambda: |-
return x == "Off";
then:
- button.press: uppatvind_off
- delay: 6s
- if:
condition:
lambda: |-
return x != "Off";
then:
- script.execute:
id: goto_speed
target: !lambda 'return x;'
script:
- id: goto_speed
mode: queued
parameters:
target: string
then:
- repeat:
count: !lambda |-
int current = 0;
int desired = 0;
auto state = id(uppatvind_mode).state;
if (state == "Low")
current = 1;
else if (state == "Medium")
current = 2;
else if (state == "High")
current = 3;
if (target == "Low")
desired = 1;
else if (target == "Medium")
desired = 2;
else if (target == "High")
desired = 3;
if (current == desired)
{
ESP_LOGI("uppatvind", "Already at target");
return 0;
}
int steps = (desired - current + 4) % 4;
ESP_LOGI(
"uppatvind",
"Current=%d Desired=%d Steps=%d",
current,
desired,
steps
);
return steps;
then:
- button.press: uppatvind_button
- delay: 1500ms
button:
- platform: template
name: "UPPATVIND Next"
id: uppatvind_button
on_press:
then:
- output.turn_on: fan_button
- delay: 250ms
- output.turn_off: fan_button
- platform: template
name: "UPPATVIND OFF"
id: uppatvind_off
on_press:
then:
- output.turn_on: fan_button
- delay: 4000ms
- output.turn_off: fan_button
Home Assistant Integration
Possible automations:
- Start purifier remote when 3d Printer is running
- Enable purifier when PM2.5 max target is reached
- Enable higher speed when PM2.5 increases
- Turn off when PM2.5 min target is reached
- Turn off when 3d printer is not running
- Combine with air quality sensors
- Dashboard integration
type: custom:stack-in-card
cards:
- type: custom:mushroom-template-card
primary: UPPATVIND I
secondary: >
{% if is_state('binary_sensor.uppatvind_filter_warning','on')
%}
⚠ Filter Warning
{% else %}
Filter OK
{% endif %}
icon: mdi:air-purifier
- type: custom:mushroom-chips-card
chips:
- type: template
icon: mdi:fan-off
entity: select.uppatvind_speed
icon_color: >
{% if is_state('select.uppatvind_speed','Off') %} red {%
endif %}
tap_action:
action: perform-action
perform_action: select.select_option
target:
entity_id: select.uppatvind_speed
data:
option: "Off"
- type: template
icon: mdi:fan-speed-1
entity: select.uppatvind_speed
icon_color: >
{% if is_state('select.uppatvind_speed','Low') %} green {%
endif %}
tap_action:
action: perform-action
perform_action: select.select_option
target:
entity_id: select.uppatvind_speed
data:
option: Low
- type: template
icon: mdi:fan-speed-2
entity: select.ppatvind_speed
icon_color: >
{% if is_state('select.uppatvind_speed','Medium') %} orange
{% endif %}
tap_action:
action: perform-action
perform_action: select.select_option
target:
entity_id: select.uppatvind_speed
data:
option: Medium
- type: template
icon: mdi:fan-speed-3
entity: select.uppatvind_speed
icon_color: >
{% if is_state('select.uppatvind_speed','High') %} red {%
endif %}
tap_action:
action: perform-action
perform_action: select.select_option
target:
entity_id: select.uppatvind_speed
data:
option: High
Calibration
The LED/PWM signal values may vary between purifier revisions.
After flashing ESPHome:
- Monitor pulse width values
- Record values for:
- OFF
- Speed 1
- Speed 2
- Speed 3
- Adjust thresholds in ESPHome YAML
Safety Notes
- Disconnect power before soldering
- Verify voltages with a multimeter
- ESP32-C3 GPIOs are NOT 5V tolerant
- Avoid short circuits inside the purifier
Future Improvements
- Fully native fan control
- Automatic air quality mode
- ESPHome dashboard improvements
- Internal USB-C power mod