Infineon TeamMaysa Ouaja
Published © MIT

Smart Bladeless Fan with Air Purification & Home Assistant

Smart Bladeless Fan with Air Purification, Filter Monitoring & Home Assistant Integration using PSOC™ 6.

IntermediateFull instructions provided20 hours172
Smart Bladeless Fan with Air Purification & Home Assistant

Things used in this project

Hardware components

PSOC™ 6 AI Evaluation Kit (CY8CKIT-062S2-AI)
Infineon PSOC™ 6 AI Evaluation Kit (CY8CKIT-062S2-AI)
×1
Daughter Board, BTS50010-1TAD PROFET High-Side Switch
Daughter Board, BTS50010-1TAD PROFET High-Side Switch
×1
SUNON Ventilator
×1
S2GO PRESSURE DPS368
Infineon S2GO PRESSURE DPS368
×1
Ikea Air Quality Sensor and Hub
×1

Software apps and online services

MicroPython
MicroPython
Home Assistant
Home Assistant

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Filter Holder

Base

Interior

Should be scaled to 170.78

Exterior

Should be scaled to 170.78

Schematics

smartfan_tqrotLLfvW.fzz

Code

Code

MicroPython
from machine import Pin, PWM, I2C
import network
import time
import mip
import wifi_secrets
import mqtt_secrets
import DPS
try:
    from umqtt.simple import MQTTClient
except:
    mip.install("umqtt.simple")
    from umqtt.simple import MQTTClient
try:
    import uhome
except:
    mip.install("github:ederjc/uhome/uhome/uhome.py")
    import uhome

# ---------- FAN SETTINGS ----------
FAN_PWM_PIN = "P9_7"
PWM_FREQUENCY = 29000
MAX_DUTY = 65535
PWM_FOR_30_PERCENT = 65
PWM_FOR_100_PERCENT = 100
KICKSTART_PCT = 80
KICKSTART_MS = 1000
pwm = PWM(Pin(FAN_PWM_PIN), freq=PWM_FREQUENCY, duty_u16=0)
current_ha_speed = 0

# ---------- FILTER THRESHOLDS ----------
CLEAN_BASELINE    = 150
THRESHOLD_WARNING = CLEAN_BASELINE * 1.15
THRESHOLD_DIRTY   = CLEAN_BASELINE * 1.30
THRESHOLD_REPLACE = CLEAN_BASELINE * 1.50
FILTER_UPDATE_INTERVAL_MS = 5000
last_filter_update = 0

STATUS_MAP = {
    "CLEAN": 0,
    "GETTING DIRTY": 1,
    "DIRTY - Clean Soon!": 2,
    "REPLACE FILTER NOW!": 3
}

# ---------- FAN FUNCTIONS ----------
def set_pwm(percent):
    duty = int((percent / 100) * MAX_DUTY)
    pwm.duty_u16(duty)

def set_fan_from_home_assistant(value):
    global current_ha_speed
    value = int(float(value))
    if value <= 0:
        current_ha_speed = 0
        set_pwm(0)
    elif value <= 50:
        current_ha_speed = 40
        set_pwm(KICKSTART_PCT)
        time.sleep_ms(KICKSTART_MS)
        set_pwm(PWM_FOR_30_PERCENT)
    else:
        current_ha_speed = 100
        set_pwm(KICKSTART_PCT)
        time.sleep_ms(KICKSTART_MS)
        set_pwm(PWM_FOR_100_PERCENT)
    print("HA speed:", current_ha_speed, "%")
    fan_speed.publish(current_ha_speed)

# ---------- SENSOR FUNCTIONS ----------
def get_pressure_drop():
    p_before = sensor_before.measurePressureOnce()
    p_after  = sensor_after.measurePressureOnce()
    return p_before - p_after

def get_filter_status(drop):
    if drop < THRESHOLD_WARNING:
        return "CLEAN"
    elif drop < THRESHOLD_DIRTY:
        return "GETTING DIRTY"
    elif drop < THRESHOLD_REPLACE:
        return "DIRTY - Clean Soon!"
    else:
        return "REPLACE FILTER NOW!"

# ---------- WIFI ----------
def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    print("Connecting to WiFi...")
    wlan.connect(wifi_secrets.ssid, wifi_secrets.psk)
    while not wlan.isconnected():
        print(".", end="")
        time.sleep(1)
    print("\nWiFi connected")
    print("PSoC IP:", wlan.ifconfig()[0])

# ---------- MAIN ----------
connect_wifi()
device = uhome.Device("Smart Fan")
mqttc = MQTTClient(
    device.id,
    mqtt_secrets.broker,
    user=mqtt_secrets.user,
    password=mqtt_secrets.password,
    keepalive=60
)
device.connect(mqttc)

fan_speed = uhome.Number(
    device,
    "Speed",
    min=0,
    max=100,
    step=1,
    unit_of_measurement="%",
    mode="slider",
    icon="mdi:fan"
)
fan_speed.set_action(set_fan_from_home_assistant)

filter_status_num = uhome.Number(
    device,
    "Filter Status",
    min=0,
    max=3,
    step=1,
    unit_of_measurement="",
    mode="box",
    icon="mdi:air-filter"
)
device.discover_all()
print("Ready!")

# ---------- SENSORS ----------
print("Initializing pressure sensors...")
i2c = I2C(scl=Pin('P0_2'), sda=Pin('P0_3'))
sensor_before = DPS.DPS(i2c, addr=0x77)
sensor_after  = DPS.DPS(i2c, addr=0x76)
print("Sensors ready!")
print("Smart Fan + Filter Monitor ready\n")

# ---------- MAIN LOOP ----------
while True:
    device.loop()
    now = time.ticks_ms()
    if time.ticks_diff(now, last_filter_update) >= FILTER_UPDATE_INTERVAL_MS:
        last_filter_update = now
        try:
            drop   = get_pressure_drop()
            status = get_filter_status(drop)
            print(f"Pressure Drop: {drop:.2f} Pa | Status: {status}")
            filter_status_num.publish(STATUS_MAP[status])
        except Exception as e:
            print(f"Filter sensor ERROR: {e}")
    time.sleep_ms(100)

Credits

Infineon Team
143 projects • 429 followers
Hands-on projects, tutorials, and code for sensors, MCUs, connectivity, security, power, and IoT. Follow for new builds and ideas.
Maysa Ouaja
2 projects • 6 followers
Thanks to k-eye.

Comments