From a3cd6bac1676af375e8eb2c21e11ff7efb31b1a4 Mon Sep 17 00:00:00 2001 From: dawie Date: Fri, 30 Jan 2026 18:41:07 +0200 Subject: [PATCH] Fix fan curve saving - use flat config structure for WLED compatibility - Changed from nested JSON array to flat curve-t1/s1, curve-t2/s2, etc. - Added appendConfigData() for config page labels - Reduced max curve points to 5 for simpler config - Added bounds checking for speed values --- .../GPU_Fan_Controller/GPU_Fan_Controller.cpp | 156 ++++++++++++------ 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/usermods/GPU_Fan_Controller/GPU_Fan_Controller.cpp b/usermods/GPU_Fan_Controller/GPU_Fan_Controller.cpp index 917c89cd..eba5028c 100644 --- a/usermods/GPU_Fan_Controller/GPU_Fan_Controller.cpp +++ b/usermods/GPU_Fan_Controller/GPU_Fan_Controller.cpp @@ -10,7 +10,7 @@ * Features: * - Web API for temperature updates from external sources (GPU, etc.) * - Fixed speed mode with configurable percentage - * - Temperature curve mode with up to 10 configurable points + * - Temperature curve mode with up to 5 configurable points * - Safety fallback to 100% if temperature data times out * - Integration with WLED's web interface * @@ -54,19 +54,24 @@ class GPUFanControllerUsermod : public Usermod { ControlMode controlMode = MODE_CURVE; uint8_t fixedSpeedPct = 50; // 0-100% - // Temperature curve (up to 10 points) - static const uint8_t MAX_CURVE_POINTS = 10; + // 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; uint8_t curveCount = 4; - struct CurvePoint { - float temp; - uint8_t speed; - }; - CurvePoint curve[MAX_CURVE_POINTS] = { - {30.0f, 30}, - {50.0f, 50}, - {70.0f, 75}, - {85.0f, 100} - }; + + // Curve point temperatures (°C) + int16_t curveTemp1 = 30; + int16_t curveTemp2 = 50; + int16_t curveTemp3 = 70; + int16_t curveTemp4 = 85; + int16_t curveTemp5 = 95; + + // Curve point speeds (%) + uint8_t curveSpeed1 = 30; + uint8_t curveSpeed2 = 50; + uint8_t curveSpeed3 = 75; + uint8_t curveSpeed4 = 100; + uint8_t curveSpeed5 = 100; // Runtime state float currentTemp = 25.0f; @@ -132,28 +137,57 @@ class GPUFanControllerUsermod : public Usermod { #endif } + // Get curve temperature at index + int16_t getCurveTemp(uint8_t idx) { + switch(idx) { + case 0: return curveTemp1; + case 1: return curveTemp2; + case 2: return curveTemp3; + case 3: return curveTemp4; + case 4: return curveTemp5; + default: return 50; + } + } + + // Get curve speed at index + uint8_t getCurveSpeed(uint8_t idx) { + switch(idx) { + case 0: return curveSpeed1; + case 1: return curveSpeed2; + case 2: return curveSpeed3; + case 3: return curveSpeed4; + case 4: return curveSpeed5; + default: return 50; + } + } + // Calculate fan speed from temperature curve uint8_t calculateCurveSpeed(float temp) { if (curveCount < 2) return 50; // Fallback // Below first point - if (temp <= curve[0].temp) { - return curve[0].speed; + if (temp <= getCurveTemp(0)) { + return getCurveSpeed(0); } // Above last point - if (temp >= curve[curveCount - 1].temp) { - return curve[curveCount - 1].speed; + if (temp >= getCurveTemp(curveCount - 1)) { + return getCurveSpeed(curveCount - 1); } // Linear interpolation between points for (uint8_t i = 0; i < curveCount - 1; i++) { - if (temp >= curve[i].temp && temp <= curve[i + 1].temp) { - float tempRange = curve[i + 1].temp - curve[i].temp; - float speedRange = curve[i + 1].speed - curve[i].speed; - float tempDiff = temp - curve[i].temp; + int16_t t1 = getCurveTemp(i); + int16_t t2 = getCurveTemp(i + 1); + uint8_t s1 = getCurveSpeed(i); + uint8_t s2 = getCurveSpeed(i + 1); + + if (temp >= t1 && temp <= t2) { + float tempRange = t2 - t1; + float speedRange = s2 - s1; + float tempDiff = temp - t1; - int speed = curve[i].speed + (int)((tempDiff / tempRange) * speedRange); + int speed = s1 + (int)((tempDiff / tempRange) * speedRange); return constrain(speed, 0, 100); } } @@ -310,16 +344,39 @@ class GPUFanControllerUsermod : public Usermod { top["pwm-pin"] = pwmPin; top["mode"] = (int)controlMode; top["fixed-speed"] = fixedSpeedPct; - top["timeout-ms"] = tempTimeoutMs; + top["timeout-ms"] = (int)tempTimeoutMs; + top["curve-points"] = curveCount; - // Save curve points - top["curve-count"] = curveCount; - JsonArray curveArr = top.createNestedArray("curve"); - for (uint8_t i = 0; i < curveCount; i++) { - JsonObject point = curveArr.createNestedObject(); - point["temp"] = curve[i].temp; - point["speed"] = curve[i].speed; - } + // Save curve points as flat values + top["curve-t1"] = curveTemp1; + top["curve-t2"] = curveTemp2; + top["curve-t3"] = curveTemp3; + top["curve-t4"] = curveTemp4; + top["curve-t5"] = curveTemp5; + top["curve-s1"] = curveSpeed1; + top["curve-s2"] = curveSpeed2; + top["curve-s3"] = curveSpeed3; + top["curve-s4"] = curveSpeed4; + top["curve-s5"] = curveSpeed5; + } + + // Append config data (labels for config page) + void appendConfigData() override { + 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: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 @@ -336,23 +393,28 @@ class GPUFanControllerUsermod : public Usermod { newPwmPin = top["pwm-pin"] | newPwmPin; controlMode = (ControlMode)(top["mode"] | (int)controlMode); fixedSpeedPct = top["fixed-speed"] | fixedSpeedPct; - tempTimeoutMs = top["timeout-ms"] | tempTimeoutMs; - - // Load curve points - curveCount = top["curve-count"] | curveCount; + tempTimeoutMs = top["timeout-ms"] | (int)tempTimeoutMs; + curveCount = top["curve-points"] | curveCount; curveCount = constrain(curveCount, 2, MAX_CURVE_POINTS); - JsonArray curveArr = top["curve"]; - if (!curveArr.isNull()) { - uint8_t i = 0; - for (JsonObject point : curveArr) { - if (i >= MAX_CURVE_POINTS) break; - curve[i].temp = point["temp"] | curve[i].temp; - curve[i].speed = point["speed"] | curve[i].speed; - i++; - } - curveCount = i; - } + // Load curve points + curveTemp1 = top["curve-t1"] | curveTemp1; + curveTemp2 = top["curve-t2"] | curveTemp2; + curveTemp3 = top["curve-t3"] | curveTemp3; + curveTemp4 = top["curve-t4"] | curveTemp4; + curveTemp5 = top["curve-t5"] | curveTemp5; + curveSpeed1 = top["curve-s1"] | curveSpeed1; + curveSpeed2 = top["curve-s2"] | curveSpeed2; + curveSpeed3 = top["curve-s3"] | curveSpeed3; + curveSpeed4 = top["curve-s4"] | curveSpeed4; + curveSpeed5 = top["curve-s5"] | curveSpeed5; + + // 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) { pwmPin = newPwmPin; @@ -366,7 +428,7 @@ class GPUFanControllerUsermod : public Usermod { } } - return !top["curve-count"].isNull(); + return !top["curve-t1"].isNull(); } uint16_t getId() override {