MeshAdv-Mini icon indicating copy to clipboard operation
MeshAdv-Mini copied to clipboard

Suggestion for PWM fan control of Raspberry Pi Zero 2W

Open jkwedar opened this issue 7 months ago • 0 comments

The commonly shared PWM fan control examples weren’t quite right for my use case on the Raspberry Pi Zero 2W. Most are designed with the Pi 4 or 5 in mind, but the MeshAdv-Mini setup with a Zero 2W has different requirements. I needed a solution that was lightweight, headless, and tailored to the specific behavior of the fan.

Setup: Raspberry Pi Zero 2W (SSH access only, no desktop environment) Raspbian GNU/Linux 12 (Bookworm) Waveshare UPS HAT (C) (optional) MeshAdv-Mini enclosure with PWM-controlled fan PWM fan (typically 3-wire: red, black, blue) connected to the MeshAdv-Mini PWM fan header

Raspberry Pi Zero 2W: Automatic Fan Control with PWM + systemd This sets up automatic fan control using a Waveshare UPS HAT and GPIO-based PWM on a Pi Zero 2W. The fan spins only when needed and stops when idle.

Step 1: Calibrate Your Fan (Optional, Recommended)

sudo nano /root/fan_calibrate.py

Paste this:

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import time

FAN_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(FAN_PIN, GPIO.OUT)

pwm = GPIO.PWM(FAN_PIN, 25)
pwm.start(0)

print("Enter fan speed (0-100). Type 'exit' or press Ctrl+C to quit.")

try:
    while True:
        val = input("Set fan speed: ").strip()
        if val.lower() == 'exit':
            break
        try:
            dc = int(val)
            if 0 <= dc <= 100:
                pwm.ChangeDutyCycle(dc)
            else:
                print("Enter a value from 0 to 100.")
        except:
            print("Invalid input.")
except KeyboardInterrupt:
    pass
finally:
    pwm.ChangeDutyCycle(0)
    pwm.stop()
    GPIO.cleanup()
    print("Fan off. GPIO cleaned up.")

Then make it executable and run:

sudo chmod +x /root/fan_calibrate.py
sudo /root/fan_calibrate.py

Find the lowest duty cycle where the fan reliably starts (e.g., 25%) and remember that value.

Step 2: Create the Fan Control Script

sudo nano /usr/local/bin/fan_control.py

Paste this:

#!/usr/bin/env python3

import time
import RPi.GPIO as GPIO

FAN_PIN = 18
FAN_MIN = 25  # Minimum duty cycle for reliable spin

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(FAN_PIN, GPIO.OUT)
pwm = GPIO.PWM(FAN_PIN, 25)
pwm.start(0)

def get_cpu_temp():
    try:
        with open("/sys/class/thermal/thermal_zone0/temp", "r") as f:
            return float(f.read()) / 1000.0
    except:
        return 0.0

try:
    while True:
        temp = get_cpu_temp()

        if temp < 45:
            fan_speed = 0
        elif temp > 70:
            fan_speed = 100
        else:
            fan_speed = (temp - 45) * 4

        if 0 < fan_speed < FAN_MIN:
            fan_speed = FAN_MIN

        pwm.ChangeDutyCycle(fan_speed)
        print(f"CPU Temp: {temp:.1f}°C | Fan: {int(fan_speed)}%")
        time.sleep(15)

except KeyboardInterrupt:
    pass

finally:
    pwm.ChangeDutyCycle(0)
    pwm.stop()
    GPIO.cleanup()

Then: sudo chmod +x /usr/local/bin/fan_control.py

Step 3: Create the systemd Service

sudo nano /etc/systemd/system/fan_control.service

Paste this:

[Unit]
Description=MeshAdv Mini Fan Control Script
After=network.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/fan_control.py
Restart=on-failure
User=root

[Install]
WantedBy=multi-user.target

Then enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable fan_control.service
sudo systemctl start fan_control.service

Verifying It Works

Watch live output: sudo journalctl -u fan_control.service -f

Check CPU temp:

cat /sys/class/thermal/thermal_zone0/temp
(straight °C = divide by 1000)

Stress test (optional):

yes > /dev/null &
yes > /dev/null &

Let temp rise above 45°C — the fan should start. Then: killall yes

**Note: This solution was developed and tested in my specific environment with the help of ChatGPT. It works reliably on a Raspberry Pi Zero 2W running Raspbian Bookworm with the MeshAdv-Mini and a PWM-controlled fan. Your setup may require minor adjustments, especially if your Python environment or GPIO configuration differs. Always test and calibrate based on your specific hardware.

jkwedar avatar Jul 16 '25 20:07 jkwedar