Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 36 additions & 26 deletions usermods/PWM_fan/PWM_fan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,23 @@ class PWMFanUsermod : public Usermod {
int8_t tachoPin = TACHO_PIN;
int8_t pwmPin = PWM_PIN;
uint8_t tachoUpdateSec = 30;
float targetTemperature = 35.0;
float targetTemperature = 34.5;
float maxSpeedTemperature = 38;
uint8_t minPWMValuePct = 0;
uint8_t maxPWMValuePct = 100;
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
uint8_t pwmValuePct = 0;

// constant values
static const uint8_t _pwmMaxValue = 255;
static const uint8_t _pwmMaxStepCount = 7;
float _pwmTempStepSize = 0.5f;


// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _enabled[];
static const char _tachoPin[];
static const char _pwmPin[];
static const char _temperature[];
static const char _temperatureMax[];
static const char _tachoUpdateSec[];
static const char _minPWMValuePct[];
static const char _maxPWMValuePct[];
Expand All @@ -96,17 +96,15 @@ class PWMFanUsermod : public Usermod {
tachoPin = -1;
}

void updateTacho(void) {
// store milliseconds when tacho was measured the last time
msLastTachoMeasurement = millis();
void updateTacho(unsigned long msInterval) {
if (tachoPin < 0) return;

// start of tacho measurement
// detach interrupt while calculating rpm
detachInterrupt(digitalPinToInterrupt(tachoPin));
// calculate rpm
last_rpm = (counter_rpm * 60) / numberOfInterrupsInOneSingleRotation;
last_rpm /= tachoUpdateSec;
last_rpm = (last_rpm * 1000) / msInterval;
// reset counter
counter_rpm = 0;
// attach interrupt again
Expand Down Expand Up @@ -167,25 +165,30 @@ class PWMFanUsermod : public Usermod {

void setFanPWMbasedOnTemperature(void) {
float temp = getActualTemperature();
// dividing minPercent and maxPercent into equal pwmvalue sizes
int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
int pwmStep = calculatePwmStep(temp - targetTemperature);
// minimum based on full speed - not entered MaxPercent
int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
}

uint8_t calculatePwmStep(float diffTemp){
if ((diffTemp == NAN) || (diffTemp <= -100.0)) {
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
return _pwmMaxStepCount;
int pwmMinValue = minPWMValuePct * _pwmMaxValue / 100;
int pwmMaxValue = maxPWMValuePct * _pwmMaxValue / 100;

if (pwmMaxValue <= pwmMinValue) {
updateFanSpeed(_pwmMaxValue); // fail safe: invalid config, run at max speed
return;
}
if(diffTemp <=0){
return 0;

int pwmRange = pwmMaxValue - pwmMinValue;


if (temp < targetTemperature) {
updateFanSpeed(pwmMinValue);
} else if(temp > maxSpeedTemperature) {
updateFanSpeed(pwmMaxValue);
} else {
if (maxSpeedTemperature <= targetTemperature) {
updateFanSpeed(pwmMaxValue); // fail safe: invalid config, run at max speed
return;
}
float speedFactor = (temp - targetTemperature) / (maxSpeedTemperature - targetTemperature); // 0 - 1
updateFanSpeed(speedFactor * pwmRange + pwmMinValue);
}
int calculatedStep = (diffTemp / _pwmTempStepSize)+1;
// anything greater than max stepcount gets max
return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep);
}

public:
Expand Down Expand Up @@ -218,8 +221,11 @@ class PWMFanUsermod : public Usermod {
unsigned long now = millis();
if ((now - msLastTachoMeasurement) < (tachoUpdateSec * 1000)) return;

updateTacho();
updateTacho(now - msLastTachoMeasurement);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's bound to be roughly 30s due to the statement above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That assumes that the call is effectively called every 30s. It could throw off the result if this is not the case due to high load.

It's a little improvement to make the code more robust.

if (!lockFan) setFanPWMbasedOnTemperature();

// store milliseconds when tacho was measured the last time
msLastTachoMeasurement = now;
}

/*
Expand Down Expand Up @@ -257,7 +263,8 @@ class PWMFanUsermod : public Usermod {
JsonArray data = user.createNestedArray(F("Speed"));
if (tachoPin >= 0) {
data.add(last_rpm);
data.add(F("rpm"));
if (lockFan) data.add(F(" rpm (locked)"));
else data.add(F(" rpm (auto)"));
} else {
if (lockFan) data.add(F("locked"));
else data.add(F("auto"));
Expand Down Expand Up @@ -316,6 +323,7 @@ class PWMFanUsermod : public Usermod {
top[FPSTR(_tachoPin)] = tachoPin;
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
top[FPSTR(_temperature)] = targetTemperature;
top[FPSTR(_temperatureMax)] = maxSpeedTemperature;
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
Expand Down Expand Up @@ -349,6 +357,7 @@ class PWMFanUsermod : public Usermod {
tachoUpdateSec = top[FPSTR(_tachoUpdateSec)] | tachoUpdateSec;
tachoUpdateSec = (uint8_t) max(1,(int)tachoUpdateSec); // bounds checking
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
maxSpeedTemperature = top[FPSTR(_temperatureMax)] | maxSpeedTemperature;
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct;
Expand Down Expand Up @@ -395,6 +404,7 @@ const char PWMFanUsermod::_enabled[] PROGMEM = "enabled";
const char PWMFanUsermod::_tachoPin[] PROGMEM = "tacho-pin";
const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
const char PWMFanUsermod::_temperatureMax[] PROGMEM = "max-temp-C";
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
const char PWMFanUsermod::_maxPWMValuePct[] PROGMEM = "max-PWM-percent";
Expand Down