Add /gpu-fan/save endpoint with serializeConfig()

- New POST endpoint handles JSON body with curve config
- Calls serializeConfig() to persist to flash
- Simplified and cleaned up code
This commit is contained in:
dawie 2026-01-30 19:42:35 +02:00
parent fdcd26b3ce
commit 211118f8ff

View File

@ -1,44 +1,16 @@
#include "wled.h" #include "wled.h"
#include "gpu_fan_html.h" #include "gpu_fan_html.h"
/*
* GPU Fan Controller Usermod for WLED
*
* This usermod controls a PWM fan based on GPU temperature received via HTTP API
* from a Python monitoring script. It supports fixed speed mode and temperature
* curve-based control.
*
* Features:
* - Web API for temperature updates from external sources (GPU, etc.)
* - Fixed speed mode with configurable percentage
* - Temperature curve mode with up to 5 configurable points
* - Visual curve editor at /gpu-fan
* - Safety fallback to 100% if temperature data times out
* - Integration with WLED's web interface
*
* Connections:
* - Fan GND -> ESP32 GND
* - Fan +12V -> 12V Power Supply
* - Fan PWM -> Configured GPIO (default: GPIO 13)
*
* API Endpoints:
* - POST /json/state with JSON: {"GPU-Fan": {"temperature": 65.5}}
* - GET /json/info
* - GET /gpu-fan (curve editor page)
*/
#ifndef PWM_FAN_PIN #ifndef PWM_FAN_PIN
#define PWM_FAN_PIN 13 #define PWM_FAN_PIN 13
#endif #endif
// PWM configuration for 4-pin PC fans (Intel spec: 25kHz)
#define GPU_FAN_PWM_FREQ 25000 #define GPU_FAN_PWM_FREQ 25000
#define GPU_FAN_PWM_RESOLUTION 8 #define GPU_FAN_PWM_RESOLUTION 8
class GPUFanControllerUsermod : public Usermod { class GPUFanControllerUsermod : public Usermod {
private: private:
// Configuration
bool enabled = true; bool enabled = true;
bool initDone = false; bool initDone = false;
bool webHandlerRegistered = false; bool webHandlerRegistered = false;
@ -48,53 +20,43 @@ class GPUFanControllerUsermod : public Usermod {
uint8_t pwmChannel = 255; uint8_t pwmChannel = 255;
#endif #endif
// Control modes
enum ControlMode { enum ControlMode {
MODE_FIXED = 0, MODE_FIXED = 0,
MODE_CURVE = 1 MODE_CURVE = 1
}; };
// Fan configuration
ControlMode controlMode = MODE_CURVE; ControlMode controlMode = MODE_CURVE;
uint8_t fixedSpeedPct = 50; // 0-100% uint8_t fixedSpeedPct = 50;
// Temperature curve - using flat structure for WLED config compatibility
// 5 curve points with separate temp and speed values
static const uint8_t MAX_CURVE_POINTS = 5; static const uint8_t MAX_CURVE_POINTS = 5;
uint8_t curveCount = 4; uint8_t curveCount = 4;
// Curve point temperatures (°C)
int16_t curveTemp1 = 30; int16_t curveTemp1 = 30;
int16_t curveTemp2 = 50; int16_t curveTemp2 = 50;
int16_t curveTemp3 = 70; int16_t curveTemp3 = 70;
int16_t curveTemp4 = 85; int16_t curveTemp4 = 85;
int16_t curveTemp5 = 95; int16_t curveTemp5 = 95;
// Curve point speeds (%)
uint8_t curveSpeed1 = 30; uint8_t curveSpeed1 = 30;
uint8_t curveSpeed2 = 50; uint8_t curveSpeed2 = 50;
uint8_t curveSpeed3 = 75; uint8_t curveSpeed3 = 75;
uint8_t curveSpeed4 = 100; uint8_t curveSpeed4 = 100;
uint8_t curveSpeed5 = 100; uint8_t curveSpeed5 = 100;
// Runtime state
float currentTemp = 25.0f; float currentTemp = 25.0f;
uint8_t currentPWM = 0; uint8_t currentPWM = 0;
unsigned long lastTempUpdate = 0; unsigned long lastTempUpdate = 0;
unsigned long tempTimeoutMs = 10000; // 10 second timeout unsigned long tempTimeoutMs = 10000;
unsigned long lastLoopTime = 0; unsigned long lastLoopTime = 0;
unsigned long loopIntervalMs = 2000; // Update every 2 seconds unsigned long loopIntervalMs = 2000;
// String constants
static const char _name[]; static const char _name[];
static const char _enabled[]; static const char _enabled[];
// Initialize PWM
void initPWM() { void initPWM() {
if (pwmPin < 0 || !PinManager::allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) { if (pwmPin < 0 || !PinManager::allocatePin(pwmPin, true, PinOwner::UM_Unspecified)) {
enabled = false; enabled = false;
pwmPin = -1; pwmPin = -1;
DEBUG_PRINTLN(F("GPU Fan: PWM pin allocation failed"));
return; return;
} }
@ -105,20 +67,15 @@ class GPUFanControllerUsermod : public Usermod {
pwmChannel = PinManager::allocateLedc(1); pwmChannel = PinManager::allocateLedc(1);
if (pwmChannel == 255) { if (pwmChannel == 255) {
deinitPWM(); deinitPWM();
DEBUG_PRINTLN(F("GPU Fan: LEDC channel allocation failed"));
return; return;
} }
ledcSetup(pwmChannel, GPU_FAN_PWM_FREQ, GPU_FAN_PWM_RESOLUTION); ledcSetup(pwmChannel, GPU_FAN_PWM_FREQ, GPU_FAN_PWM_RESOLUTION);
ledcAttachPin(pwmPin, pwmChannel); ledcAttachPin(pwmPin, pwmChannel);
#endif #endif
DEBUG_PRINTLN(F("GPU Fan: PWM initialized successfully"));
} }
// Deinitialize PWM
void deinitPWM() { void deinitPWM() {
if (pwmPin < 0) return; if (pwmPin < 0) return;
PinManager::deallocatePin(pwmPin, PinOwner::UM_Unspecified); PinManager::deallocatePin(pwmPin, PinOwner::UM_Unspecified);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
if (pwmChannel != 255) { if (pwmChannel != 255) {
@ -129,10 +86,8 @@ class GPUFanControllerUsermod : public Usermod {
pwmPin = -1; pwmPin = -1;
} }
// Set fan speed (0-255 PWM value)
void setFanPWM(uint8_t pwmValue) { void setFanPWM(uint8_t pwmValue) {
if (!enabled || pwmPin < 0) return; if (!enabled || pwmPin < 0) return;
currentPWM = pwmValue; currentPWM = pwmValue;
#ifdef ESP8266 #ifdef ESP8266
analogWrite(pwmPin, pwmValue); analogWrite(pwmPin, pwmValue);
@ -141,7 +96,6 @@ class GPUFanControllerUsermod : public Usermod {
#endif #endif
} }
// Get curve temperature at index
int16_t getCurveTemp(uint8_t idx) { int16_t getCurveTemp(uint8_t idx) {
switch(idx) { switch(idx) {
case 0: return curveTemp1; case 0: return curveTemp1;
@ -153,7 +107,6 @@ class GPUFanControllerUsermod : public Usermod {
} }
} }
// Get curve speed at index
uint8_t getCurveSpeed(uint8_t idx) { uint8_t getCurveSpeed(uint8_t idx) {
switch(idx) { switch(idx) {
case 0: return curveSpeed1; case 0: return curveSpeed1;
@ -165,21 +118,12 @@ class GPUFanControllerUsermod : public Usermod {
} }
} }
// Calculate fan speed from temperature curve
uint8_t calculateCurveSpeed(float temp) { uint8_t calculateCurveSpeed(float temp) {
if (curveCount < 2) return 50; // Fallback if (curveCount < 2) return 50;
// Below first point if (temp <= getCurveTemp(0)) return getCurveSpeed(0);
if (temp <= getCurveTemp(0)) { if (temp >= getCurveTemp(curveCount - 1)) return getCurveSpeed(curveCount - 1);
return getCurveSpeed(0);
}
// Above last point
if (temp >= getCurveTemp(curveCount - 1)) {
return getCurveSpeed(curveCount - 1);
}
// Linear interpolation between points
for (uint8_t i = 0; i < curveCount - 1; i++) { for (uint8_t i = 0; i < curveCount - 1; i++) {
int16_t t1 = getCurveTemp(i); int16_t t1 = getCurveTemp(i);
int16_t t2 = getCurveTemp(i + 1); int16_t t2 = getCurveTemp(i + 1);
@ -187,40 +131,19 @@ class GPUFanControllerUsermod : public Usermod {
uint8_t s2 = getCurveSpeed(i + 1); uint8_t s2 = getCurveSpeed(i + 1);
if (temp >= t1 && temp <= t2) { if (temp >= t1 && temp <= t2) {
float tempRange = t2 - t1; float ratio = (temp - t1) / (float)(t2 - t1);
float speedRange = s2 - s1; return s1 + (uint8_t)(ratio * (s2 - s1));
float tempDiff = temp - t1;
int speed = s1 + (int)((tempDiff / tempRange) * speedRange);
return constrain(speed, 0, 100);
} }
} }
return 50;
return 50; // Fallback
} }
// Update fan speed based on current mode and temperature
void updateFanSpeed() { void updateFanSpeed() {
uint8_t targetSpeedPct; uint8_t targetSpeedPct = (controlMode == MODE_FIXED) ? fixedSpeedPct : calculateCurveSpeed(currentTemp);
if (controlMode == MODE_FIXED) {
targetSpeedPct = fixedSpeedPct;
} else {
targetSpeedPct = calculateCurveSpeed(currentTemp);
}
// Convert percentage to PWM (0-255)
uint8_t targetPWM = map(constrain(targetSpeedPct, 0, 100), 0, 100, 0, 255); uint8_t targetPWM = map(constrain(targetSpeedPct, 0, 100), 0, 100, 0, 255);
if (targetPWM != currentPWM) setFanPWM(targetPWM);
if (targetPWM != currentPWM) {
setFanPWM(targetPWM);
DEBUG_PRINTF("GPU Fan: %d%% (PWM: %d) | Temp: %.1f°C | Mode: %s\n",
targetSpeedPct, currentPWM, currentTemp,
controlMode == MODE_FIXED ? "Fixed" : "Curve");
}
} }
// Register web handler - call this once
void registerWebHandler() { void registerWebHandler() {
if (webHandlerRegistered) return; if (webHandlerRegistered) return;
@ -228,26 +151,50 @@ class GPUFanControllerUsermod : public Usermod {
request->send_P(200, "text/html", GPU_FAN_HTML); request->send_P(200, "text/html", GPU_FAN_HTML);
}); });
// Custom save endpoint
server.on("/gpu-fan/save", HTTP_POST,
[](AsyncWebServerRequest *request) {
request->send(200, "application/json", "{\"success\":true}");
},
NULL,
[this](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
DynamicJsonDocument doc(1024);
if (deserializeJson(doc, (const char*)data, len)) return;
if (doc.containsKey("mode")) controlMode = (ControlMode)doc["mode"].as<int>();
if (doc.containsKey("fixed-speed")) fixedSpeedPct = doc["fixed-speed"].as<int>();
if (doc.containsKey("curve-points")) curveCount = constrain(doc["curve-points"].as<int>(), 2, 5);
if (doc.containsKey("curve-t1")) curveTemp1 = doc["curve-t1"].as<int>();
if (doc.containsKey("curve-t2")) curveTemp2 = doc["curve-t2"].as<int>();
if (doc.containsKey("curve-t3")) curveTemp3 = doc["curve-t3"].as<int>();
if (doc.containsKey("curve-t4")) curveTemp4 = doc["curve-t4"].as<int>();
if (doc.containsKey("curve-t5")) curveTemp5 = doc["curve-t5"].as<int>();
if (doc.containsKey("curve-s1")) curveSpeed1 = constrain(doc["curve-s1"].as<int>(), 0, 100);
if (doc.containsKey("curve-s2")) curveSpeed2 = constrain(doc["curve-s2"].as<int>(), 0, 100);
if (doc.containsKey("curve-s3")) curveSpeed3 = constrain(doc["curve-s3"].as<int>(), 0, 100);
if (doc.containsKey("curve-s4")) curveSpeed4 = constrain(doc["curve-s4"].as<int>(), 0, 100);
if (doc.containsKey("curve-s5")) curveSpeed5 = constrain(doc["curve-s5"].as<int>(), 0, 100);
serializeConfig();
updateFanSpeed();
}
);
webHandlerRegistered = true; webHandlerRegistered = true;
DEBUG_PRINTLN(F("GPU Fan: Web handler registered at /gpu-fan"));
} }
public: public:
void setup() override { void setup() override {
initPWM(); initPWM();
setFanPWM((fixedSpeedPct * 255) / 100); // Initial speed setFanPWM((fixedSpeedPct * 255) / 100);
lastTempUpdate = millis(); lastTempUpdate = millis();
initDone = true; initDone = true;
// Register web handler immediately during setup
registerWebHandler(); registerWebHandler();
DEBUG_PRINTLN(F("GPU Fan Controller initialized"));
} }
void connected() override { void connected() override {
// Also try to register here in case setup was too early
registerWebHandler(); registerWebHandler();
} }
@ -256,110 +203,75 @@ class GPUFanControllerUsermod : public Usermod {
unsigned long now = millis(); unsigned long now = millis();
// Check for temperature timeout in curve mode
if (controlMode == MODE_CURVE && (now - lastTempUpdate > tempTimeoutMs)) { if (controlMode == MODE_CURVE && (now - lastTempUpdate > tempTimeoutMs)) {
if (currentPWM != 255) { if (currentPWM != 255) setFanPWM(255);
setFanPWM(255);
DEBUG_PRINTLN(F("GPU Fan: Temperature timeout - running at 100% for safety"));
}
return; return;
} }
// Update fan speed periodically
if (now - lastLoopTime > loopIntervalMs) { if (now - lastLoopTime > loopIntervalMs) {
updateFanSpeed(); updateFanSpeed();
lastLoopTime = now; lastLoopTime = now;
} }
} }
// Handle HTTP API requests bool handleButton(uint8_t b) override { return false; }
bool handleButton(uint8_t b) override {
// Not used for this usermod
return false;
}
// Add info to the WLED info panel
void addToJsonInfo(JsonObject& root) override { void addToJsonInfo(JsonObject& root) override {
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u"); if (user.isNull()) user = root.createNestedObject("u");
// Add enable/disable button and link to curve editor
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); JsonArray infoArr = user.createNestedArray(FPSTR(_name));
String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({'"); String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({'");
uiDomString += FPSTR(_name); uiDomString += FPSTR(_name);
uiDomString += F("':{'"); uiDomString += F("':{'enabled':");
uiDomString += FPSTR(_enabled);
uiDomString += F("':");
uiDomString += enabled ? "false" : "true"; uiDomString += enabled ? "false" : "true";
uiDomString += F("}});\"><i class=\"icons "); uiDomString += F("}});\"><i class=\"icons ");
uiDomString += enabled ? "on" : "off"; uiDomString += enabled ? "on" : "off";
uiDomString += F("\">&#xe08f;</i></button>"); uiDomString += F("\">&#xe08f;</i></button> <a href=\"/gpu-fan\" target=\"_blank\" style=\"color:#e94560\">[Editor]</a>");
uiDomString += F(" <a href=\"/gpu-fan\" target=\"_blank\" style=\"color:#e94560;font-size:0.9em;\">[Editor]</a>");
infoArr.add(uiDomString); infoArr.add(uiDomString);
if (enabled) { if (enabled) {
// Temperature display
JsonArray tempArr = user.createNestedArray(F("GPU Temp")); JsonArray tempArr = user.createNestedArray(F("GPU Temp"));
tempArr.add(currentTemp); tempArr.add(currentTemp);
tempArr.add(F("°C")); tempArr.add(F("°C"));
// Fan speed display
JsonArray speedArr = user.createNestedArray(F("Fan Speed")); JsonArray speedArr = user.createNestedArray(F("Fan Speed"));
uint8_t speedPct = (currentPWM * 100) / 255; speedArr.add((currentPWM * 100) / 255);
speedArr.add(speedPct);
speedArr.add(F("%")); speedArr.add(F("%"));
// Mode display
JsonArray modeArr = user.createNestedArray(F("Fan Mode")); JsonArray modeArr = user.createNestedArray(F("Fan Mode"));
modeArr.add(controlMode == MODE_FIXED ? F("Fixed") : F("Curve")); modeArr.add(controlMode == MODE_FIXED ? F("Fixed") : F("Curve"));
// Manual speed slider
JsonArray sliderArr = user.createNestedArray(F("Manual"));
String sliderStr = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({'");
sliderStr += FPSTR(_name);
sliderStr += F("':{'speed':parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=100 min=0 type=\"range\" value=");
sliderStr += String(fixedSpeedPct);
sliderStr += F(" /><div class=\"sliderdisplay\"></div></div></div>");
sliderArr.add(sliderStr);
} }
} }
// Handle state changes from JSON API
void readFromJsonState(JsonObject& root) override { void readFromJsonState(JsonObject& root) override {
if (!initDone) return; if (!initDone) return;
JsonObject usermod = root[FPSTR(_name)]; JsonObject usermod = root[FPSTR(_name)];
if (!usermod.isNull()) { if (usermod.isNull()) return;
// Enable/disable
if (usermod[FPSTR(_enabled)].is<bool>()) { if (usermod["enabled"].is<bool>()) {
enabled = usermod[FPSTR(_enabled)].as<bool>(); enabled = usermod["enabled"].as<bool>();
if (!enabled) setFanPWM(0); if (!enabled) setFanPWM(0);
} }
// Manual speed control if (usermod["speed"].is<int>()) {
if (enabled && !usermod["speed"].isNull() && usermod["speed"].is<int>()) { fixedSpeedPct = usermod["speed"].as<int>();
fixedSpeedPct = usermod["speed"].as<int>(); controlMode = MODE_FIXED;
controlMode = MODE_FIXED; // Switch to fixed mode when manually setting speed updateFanSpeed();
updateFanSpeed(); }
}
if (usermod["mode"].is<int>()) {
// Mode selection controlMode = (ControlMode)usermod["mode"].as<int>();
if (!usermod["mode"].isNull() && usermod["mode"].is<int>()) { }
controlMode = (ControlMode)usermod["mode"].as<int>();
} if (!usermod["temperature"].isNull()) {
currentTemp = usermod["temperature"].as<float>();
// Temperature update (from external source like Python script) lastTempUpdate = millis();
if (!usermod["temperature"].isNull()) { if (controlMode == MODE_CURVE) updateFanSpeed();
currentTemp = usermod["temperature"].as<float>();
lastTempUpdate = millis();
if (controlMode == MODE_CURVE) {
updateFanSpeed();
}
}
} }
} }
// Save configuration
void addToConfig(JsonObject& root) override { void addToConfig(JsonObject& root) override {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
@ -368,8 +280,6 @@ class GPUFanControllerUsermod : public Usermod {
top["fixed-speed"] = fixedSpeedPct; top["fixed-speed"] = fixedSpeedPct;
top["timeout-ms"] = (int)tempTimeoutMs; top["timeout-ms"] = (int)tempTimeoutMs;
top["curve-points"] = curveCount; top["curve-points"] = curveCount;
// Save curve points as flat values
top["curve-t1"] = curveTemp1; top["curve-t1"] = curveTemp1;
top["curve-t2"] = curveTemp2; top["curve-t2"] = curveTemp2;
top["curve-t3"] = curveTemp3; top["curve-t3"] = curveTemp3;
@ -382,86 +292,51 @@ class GPUFanControllerUsermod : public Usermod {
top["curve-s5"] = curveSpeed5; top["curve-s5"] = curveSpeed5;
} }
// Append config data (labels for config page)
void appendConfigData() override { void appendConfigData() override {
oappend(SET_F("addInfo('GPU-Fan:pwm-pin',1,'GPIO');")); oappend(SET_F("addInfo('GPU-Fan:pwm-pin',1,'GPIO');"));
oappend(SET_F("addInfo('GPU-Fan:mode',1,'0=Fixed, 1=Curve');")); oappend(SET_F("addInfo('GPU-Fan:mode',1,'0=Fixed, 1=Curve');"));
oappend(SET_F("addInfo('GPU-Fan:fixed-speed',1,'%');")); oappend(SET_F("addInfo('GPU-Fan:fixed-speed',1,'%');"));
oappend(SET_F("addInfo('GPU-Fan:timeout-ms',1,'ms (safety timeout)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-points',1,'2-5 points');"));
oappend(SET_F("addInfo('GPU-Fan:curve-t1',1,'°C (Point 1 temp)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-s1',1,'% (Point 1 speed)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-t2',1,'°C (Point 2 temp)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-s2',1,'% (Point 2 speed)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-t3',1,'°C (Point 3 temp)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-s3',1,'% (Point 3 speed)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-t4',1,'°C (Point 4 temp)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-s4',1,'% (Point 4 speed)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-t5',1,'°C (Point 5 temp)');"));
oappend(SET_F("addInfo('GPU-Fan:curve-s5',1,'% (Point 5 speed)');"));
} }
// Load configuration
bool readFromConfig(JsonObject& root) override { bool readFromConfig(JsonObject& root) override {
int8_t newPwmPin = pwmPin; int8_t newPwmPin = pwmPin;
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) return false;
DEBUG_PRINTLN(F("GPU Fan: No config found, using defaults"));
return false;
}
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
newPwmPin = top["pwm-pin"] | newPwmPin; newPwmPin = top["pwm-pin"] | newPwmPin;
controlMode = (ControlMode)(top["mode"] | (int)controlMode); controlMode = (ControlMode)(top["mode"] | (int)controlMode);
fixedSpeedPct = top["fixed-speed"] | fixedSpeedPct; fixedSpeedPct = top["fixed-speed"] | fixedSpeedPct;
tempTimeoutMs = top["timeout-ms"] | (int)tempTimeoutMs; tempTimeoutMs = top["timeout-ms"] | (int)tempTimeoutMs;
curveCount = top["curve-points"] | curveCount; curveCount = constrain(top["curve-points"] | curveCount, 2, MAX_CURVE_POINTS);
curveCount = constrain(curveCount, 2, MAX_CURVE_POINTS);
// Load curve points
curveTemp1 = top["curve-t1"] | curveTemp1; curveTemp1 = top["curve-t1"] | curveTemp1;
curveTemp2 = top["curve-t2"] | curveTemp2; curveTemp2 = top["curve-t2"] | curveTemp2;
curveTemp3 = top["curve-t3"] | curveTemp3; curveTemp3 = top["curve-t3"] | curveTemp3;
curveTemp4 = top["curve-t4"] | curveTemp4; curveTemp4 = top["curve-t4"] | curveTemp4;
curveTemp5 = top["curve-t5"] | curveTemp5; curveTemp5 = top["curve-t5"] | curveTemp5;
curveSpeed1 = top["curve-s1"] | curveSpeed1; curveSpeed1 = constrain(top["curve-s1"] | curveSpeed1, 0, 100);
curveSpeed2 = top["curve-s2"] | curveSpeed2; curveSpeed2 = constrain(top["curve-s2"] | curveSpeed2, 0, 100);
curveSpeed3 = top["curve-s3"] | curveSpeed3; curveSpeed3 = constrain(top["curve-s3"] | curveSpeed3, 0, 100);
curveSpeed4 = top["curve-s4"] | curveSpeed4; curveSpeed4 = constrain(top["curve-s4"] | curveSpeed4, 0, 100);
curveSpeed5 = top["curve-s5"] | curveSpeed5; curveSpeed5 = constrain(top["curve-s5"] | curveSpeed5, 0, 100);
// Constrain speed values
curveSpeed1 = constrain(curveSpeed1, 0, 100);
curveSpeed2 = constrain(curveSpeed2, 0, 100);
curveSpeed3 = constrain(curveSpeed3, 0, 100);
curveSpeed4 = constrain(curveSpeed4, 0, 100);
curveSpeed5 = constrain(curveSpeed5, 0, 100);
if (!initDone) { if (!initDone) {
pwmPin = newPwmPin; pwmPin = newPwmPin;
DEBUG_PRINTLN(F("GPU Fan: Config loaded")); } else if (pwmPin != newPwmPin) {
} else { deinitPWM();
if (pwmPin != newPwmPin) { pwmPin = newPwmPin;
DEBUG_PRINTLN(F("GPU Fan: Re-initializing pins")); setup();
deinitPWM();
pwmPin = newPwmPin;
setup();
}
} }
return !top["curve-t1"].isNull(); return true;
} }
uint16_t getId() override { uint16_t getId() override { return USERMOD_ID_UNSPECIFIED; }
return USERMOD_ID_UNSPECIFIED; // Change this if you add to const.h
}
}; };
// String constants
const char GPUFanControllerUsermod::_name[] PROGMEM = "GPU-Fan"; const char GPUFanControllerUsermod::_name[] PROGMEM = "GPU-Fan";
const char GPUFanControllerUsermod::_enabled[] PROGMEM = "enabled"; const char GPUFanControllerUsermod::_enabled[] PROGMEM = "enabled";
// Register the usermod
static GPUFanControllerUsermod gpuFanController; static GPUFanControllerUsermod gpuFanController;
REGISTER_USERMOD(gpuFanController); REGISTER_USERMOD(gpuFanController);