// GPU Fan Controller — Delta QFR1212GHE (4-pin PWM, 12V) // 6 режимов: 1=STOP, 2=STANDBY, 3=LOW, 4=MED, 5=HIGH, 6=FULL(2 мин) // RPM читается через тахометр (жёлтый → D2), PWM на синий → D9 // PWM инвертирован: 0 = стоп, 99 = полная (Delta QFR1212GHE) // Фан не гаснет полностью — минимум 1000 (10%) const int FAN_PIN = 9; const int TACH_PIN = 2; volatile unsigned long tachPulses = 0; unsigned long lastRpm = 0; unsigned long lastDataTime = 0; const int MODE_STOP = 10000; const int MODE_STANDBY = 8000; const int MODE_LOW = 5000; const int MODE_MED = 3000; const int MODE_HIGH = 2500; const int MODE_FULL = 0; const int MODE_FULL_TIME = 60000; int currentPWM = MODE_STANDBY; unsigned long modeFullStart = 0; bool inModeFull = false; char buf[16]; void setup() { pinMode(FAN_PIN, OUTPUT); pinMode(TACH_PIN, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(TACH_PIN), tachISR, FALLING); Serial.begin(115200); // Timer1: Fast PWM, ICR1=10000, prescaler 64 → 10Hz TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11); TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS12); ICR1 = 10000; OCR1A = MODE_STANDBY; lastDataTime = millis(); lastRpm = millis(); Serial.println("FAN CONTROLLER READY - modes 1-6 + RPM"); } void tachISR() { tachPulses++; } void applyPWM(int pwm) { if (pwm < MODE_FULL) pwm = MODE_FULL; if (pwm > MODE_STOP) pwm = MODE_STOP; OCR1A = pwm; currentPWM = pwm; } void loop() { unsigned long now = millis(); if (Serial.available()) { int n = Serial.readBytesUntil('\n', buf, 15); if (n > 0) { buf[n] = '\0'; char c = buf[0]; if (c >= '1' && c <= '6') { lastDataTime = now; inModeFull = false; modeFullStart = 0; switch (c) { case '1': applyPWM(MODE_STOP); Serial.println("MODE: 1 STOP"); break; case '2': applyPWM(MODE_STANDBY); Serial.println("MODE: 2 STANDBY"); break; case '3': applyPWM(MODE_LOW); Serial.println("MODE: 3 LOW"); break; case '4': applyPWM(MODE_MED); Serial.println("MODE: 4 MED"); break; case '5': applyPWM(MODE_HIGH); Serial.println("MODE: 5 HIGH"); break; case '6': applyPWM(MODE_FULL); inModeFull = true; modeFullStart = now; Serial.println("MODE: 6 FULL (60s)"); break; } } if (c == 'R' || c == 'r') { noInterrupts(); unsigned long p = tachPulses; tachPulses = 0; interrupts(); Serial.print("RPM:"); Serial.println(p * 60); } if (c == 's' || c == 'S') { Serial.print("PWM:"); Serial.println(currentPWM); if (inModeFull) Serial.print("FULL:"); Serial.println((now - modeFullStart) / 1000); Serial.print("DATA:"); Serial.println((now - lastDataTime) / 1000); } } } if (inModeFull && (now - modeFullStart >= MODE_FULL_TIME)) { applyPWM(MODE_MED); inModeFull = false; Serial.println("FULL TIMEOUT -> MODE_MED"); } if (now - lastRpm >= 1000) { noInterrupts(); unsigned long p = tachPulses; tachPulses = 0; interrupts(); Serial.print("RPM:"); Serial.println(p * 60); lastRpm = now; } }