A Laravel package for controlling PWM-controlled fans.

Installs: 2

Dependents: 1

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/dept-of-scrapyard-robotics/pwm-fan-control

0.2.0 2025-10-21 20:05 UTC

This package is auto-updated.

Last update: 2025-10-21 20:07:52 UTC


README

A modern PHP 8.3+ library for controlling PWM-driven fans on embedded Linux devices. This package provides an elegant, object-oriented API with fluent method chaining, smooth ramping, temperature-based control, and coordinated effects.

Features

  • Object-Oriented API: Clean, fluent interface for fan control
  • Speed Control: Precise percentage-based speed control (0-100%)
  • Smooth Effects: Ramping, pulsing, and custom speed curves
  • Temperature Control: Automatic fan speed adjustment based on CPU temperature
  • Speed Presets: Convenient named speed levels (low, medium, high, full)
  • Type-Safe: Full PHP 8.3+ type hints and return type declarations
  • Chainable Methods: Fluent API for readable, expressive code
  • Comprehensive Testing: Full Pest v4 test suite included

Requirements

  • PHP 8.3 or higher
  • php-pwm extension installed
  • Embedded Linux device with PWM support (Jetson Orin Nano, Raspberry Pi, etc.)
  • PWM device nodes exported (e.g., /sys/class/pwm/pwmchip3/pwm0)

Installation

Install via Composer

composer require dept-of-scrapyard-robotics/pwm-fan-control

Install php-pwm Extension

Before using this library, you must install the php-pwm extension on your embedded Linux device. See the php-pwm extension installation guide for complete instructions.

Export PWM Device

Before first use, export the PWM device:

# Export PWM channel 0 on chip 3
echo 0 | sudo tee /sys/class/pwm/pwmchip3/export

# This creates /sys/class/pwm/pwmchip3/pwm0/

Core Concepts

PWMFan

A PWMFan is an object-oriented wrapper around the php-pwm extension's Pwm class. It represents a single controllable fan connected to a specific PWM device path.

Basic Usage

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

// Create a fan controller
$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

// Set fan speed to 50%
$fan->setSpeed(50.0);

// Turn on at last known speed
$fan->on();

// Turn off
$fan->off();

// Check status
echo "Speed: " . $fan->getSpeed() . "%\n";
echo "Running: " . ($fan->isRunning() ? 'Yes' : 'No') . "\n";

API Reference

Construction

public function __construct(string $pwm_device_path, int $frequency = 1000)

Creates a new PWMFan instance for the specified PWM device.

  • Parameters:
    • $pwm_device_path - Path to PWM device (e.g., /sys/class/pwm/pwmchip3/pwm0)
    • $frequency - PWM frequency in Hz (default: 1000)

Example:

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0', 1000);

Core Operations

on()

public function on(): static

Turn the fan on at the last known speed (default 50%).

$fan->on();

off()

public function off(): static

Turn the fan off (disable PWM output).

$fan->off();

setSpeed()

public function setSpeed(float $percent): static

Set the fan speed as a percentage.

  • Parameters:
    • $percent - Speed percentage (0.0 - 100.0)
  • Returns: $this for method chaining
  • Throws: InvalidArgumentException if speed is out of range
$fan->setSpeed(75.0);  // Set to 75%

getSpeed()

public function getSpeed(): float

Get the current fan speed percentage.

$speed = $fan->getSpeed();  // Returns 0.0 - 100.0

isRunning()

public function isRunning(): bool

Check if the fan is currently running.

if ($fan->isRunning()) {
    echo "Fan is on\n";
}

getStatus()

public function getStatus(): array

Get detailed fan status information.

  • Returns: Array with keys:
    • enabled (bool)
    • speed (float) - Current speed percentage
    • lastSpeed (float) - Last known speed
    • frequency (int) - PWM frequency in Hz
    • dutyCycle (float) - Duty cycle percentage
    • devicePath (string) - PWM device path
$status = $fan->getStatus();
echo "Speed: {$status['speed']}%\n";
echo "Frequency: {$status['frequency']} Hz\n";

Speed Presets

setPreset()

public function setPreset(string $preset): static

Set fan speed using named presets.

  • Parameters:
    • $preset - Preset name (case-insensitive): 'off', 'low', 'medium', 'high', 'full'
  • Preset Speeds:
    • 'off' - 0%
    • 'low' - 25%
    • 'medium' - 50%
    • 'high' - 75%
    • 'full' - 100%
$fan->setPreset('low');     // 25%
$fan->setPreset('HIGH');    // 75% (case-insensitive)
$fan->setPreset('full');    // 100%

Effects & Animations

rampUp()

public function rampUp(float $targetSpeed = 100.0, int $duration_ms = 2000, int $steps = 20): static

Gradually increase fan speed from current speed to target speed.

  • Parameters:
    • $targetSpeed - Target speed percentage (0.0 - 100.0)
    • $duration_ms - Duration in milliseconds
    • $steps - Number of intermediate steps
// Ramp from current speed to 100% over 3 seconds
$fan->rampUp(100.0, 3000, 30);

rampDown()

public function rampDown(float $targetSpeed = 0.0, int $duration_ms = 2000, int $steps = 20): static

Gradually decrease fan speed to target speed.

// Ramp down to 25% over 2 seconds
$fan->rampDown(25.0, 2000, 20);

pulse()

public function pulse(float $lowSpeed = 30.0, float $highSpeed = 80.0, int $cycles = 3, int $duration_ms = 2000): static

Create a pulsing effect by ramping between two speeds.

  • Parameters:
    • $lowSpeed - Low speed percentage
    • $highSpeed - High speed percentage
    • $cycles - Number of pulse cycles
    • $duration_ms - Duration per cycle
// Pulse between 30% and 80% three times
$fan->pulse(30.0, 80.0, 3, 2000);

Temperature Control

autoControl()

public function autoControl(float $currentTemp, float $minTemp = 40.0, float $maxTemp = 80.0, float $minSpeed = 25.0): static

Automatically adjust fan speed based on temperature.

  • Parameters:
    • $currentTemp - Current temperature in Celsius
    • $minTemp - Minimum temperature (fan off below this)
    • $maxTemp - Maximum temperature (fan at 100% above this)
    • $minSpeed - Minimum fan speed when active

Behavior:

  • Temperature ≤ $minTemp: Fan off
  • Temperature ≥ $maxTemp: Fan at 100%
  • Between: Linear interpolation from $minSpeed to 100%
$temp = $fan->getCPUTemp();
$fan->autoControl($temp, 40.0, 80.0, 25.0);

getCPUTemp()

public function getCPUTemp(int $zone = 0): float

Read CPU temperature from thermal zone.

  • Parameters:
    • $zone - Thermal zone number (default: 0)
  • Returns: Temperature in Celsius
$temp = $fan->getCPUTemp(0);  // Read from thermal_zone0
echo "CPU Temperature: {$temp}°C\n";

Usage Examples

Example 1: Basic Speed Control

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

// Set various speeds
$fan->setSpeed(25.0);  // 25%
sleep(2);

$fan->setSpeed(50.0);  // 50%
sleep(2);

$fan->setSpeed(100.0); // 100%
sleep(2);

// Turn off
$fan->off();

Example 2: Temperature-Based Control

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

// Monitor and adjust fan speed based on temperature
while (true) {
    $temp = $fan->getCPUTemp();
    $fan->autoControl($temp, 40.0, 80.0, 25.0);
    
    echo "Temp: {$temp}°C, Fan: {$fan->getSpeed()}%\n";
    sleep(2);
}

Example 3: Smooth Ramping Effect

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

// Smooth startup
$fan->rampUp(100.0, 3000, 30);  // Ramp to 100% over 3 seconds
sleep(5);

// Smooth shutdown
$fan->rampDown(0.0, 3000, 30);  // Ramp to 0% over 3 seconds

Example 4: Pulsing Effect

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

// Create breathing effect
$fan->pulse(30.0, 80.0, 5, 2000);  // 5 pulses, 2s each

Example 5: Speed Presets

use DeptOfScrapyardRobotics\PWM\Fans\PWMFan;

$fan = new PWMFan('/sys/class/pwm/pwmchip3/pwm0');

$fan->setPreset('low');     // 25%
sleep(2);

$fan->setPreset('medium');  // 50%
sleep(2);

$fan->setPreset('high');    // 75%
sleep(2);

$fan->setPreset('full');    // 100%
sleep(2);

$fan->setPreset('off');     // 0%

Testing

The library includes a comprehensive Pest v4 test suite covering unit tests and integration tests.

Running Tests

# Run all tests
composer test

# Run tests quietly
composer test:quiet

# Run with coverage report
composer test:coverage

# Run specific test file
./vendor/bin/pest tests/Unit/PWMFanTest.php

Test Structure

tests/
├── Pest.php                          # Test configuration
└── Unit/
    └── PWMFanTest.php               # PWMFan unit tests

Included Examples

The package includes three comprehensive examples:

Running Examples

# Example 1: Basic usage
sudo php examples/01_basic_usage.php

# Example 2: Temperature-based control
sudo php examples/02_temperature_control.php

# Example 3: Advanced effects
sudo php examples/03_advanced_effects.php

Example Overview

01_basic_usage.php

  • Basic on/off control
  • Speed control and adjustment
  • Speed presets
  • Status information

02_temperature_control.php

  • Reading CPU temperature
  • Automatic speed adjustment
  • Temperature monitoring loop
  • Custom temperature scenarios

03_advanced_effects.php

  • Smooth ramping (up and down)
  • Pulsing effects
  • Custom speed curves
  • Coordinated effect sequences

Troubleshooting

Extension Not Found

PHP Fatal error: Uncaught Error: Class 'Pwm\Pwm' not found

Solution: Install the php-pwm extension first.

PWM Device Not Found

RuntimeException: Device path does not exist

Solution:

  • Export the PWM device: echo 0 | sudo tee /sys/class/pwm/pwmchip3/export
  • Verify device exists: ls /sys/class/pwm/pwmchip3/pwm0
  • Ensure proper permissions (add user to gpio group)

Permission Denied

RuntimeException: Failed to write to file

Solution: Run with sudo or configure udev rules:

# /etc/udev/rules.d/99-pwm.rules
SUBSYSTEM=="pwm", KERNEL=="pwmchip*", ACTION=="add", RUN+="/bin/chgrp -R gpio /sys%p", RUN+="/bin/chmod -R g+w /sys%p"

# Reload rules
sudo udevadm control --reload-rules
sudo udevadm trigger

Thermal Zone Not Found

RuntimeException: Thermal zone 0 not found

Solution:

  • Check available zones: ls /sys/class/thermal/
  • Use correct zone number: $fan->getCPUTemp(1) for thermal_zone1

License

MIT License. See LICENSE file for details.

Credits

This library wraps the php-pwm extension, which provides low-level PWM hardware control via Linux sysfs.

Contributing

Contributions are welcome! When contributing:

  • Follow PSR-12 coding standards
  • Add tests for new features
  • Update documentation as needed
  • Ensure all tests pass before submitting pull requests

Support

For issues related to:

  • This library: Open an issue in this repository
  • php-pwm extension: See the php-pwm repository
  • Hardware setup: Consult your device's PWM configuration documentation