Arduino-Based Pump and Fan Controller for 24V PSU (OOP, 25 kHz PWM, Separate Hysteresis & Curves for Normal/Benchmark Mode)
This project uses an Arduino Nano to control two pumps and a fan cluster based on temperature measurements. It features separate hysteresis thresholds, temperature-to-PWM curves (for normal and benchmark modes), and a kickstart mechanism for pumps. The PWM outputs run at 25 kHz to avoid audible noise, and the code is organized in an object-oriented manner for easy extensibility. Feel free to test it out in the Wowki simulator (Note: Due to limitiations in the simulator, PWM output doesn't seem to work with this code (possibly too large). Code has been confirmed working on an actual Arduino Nano though.)
-
Two Operating Modes
- Normal mode: user-defined on/off thresholds and PWM curves
- Benchmark mode: steeper curves, different on/off thresholds, higher maximum PWM
- Toggled via a push button (with an LED indicator on Pin 13)
-
Object-Oriented Design
- Base class
Actuator
handles:- Two hysteresis sets (on/off temperature)
- Two curves (normal/benchmark)
- General on/off switching and linear mapping from temperature to PWM
- Pump inherits from
Actuator
and adds a kickstart period (to ensure reliable spin-up at low PWM values) - Fan inherits from
Actuator
(with no kickstart, but with separate hysteresis and curves if desired)
- Base class
-
NTC Temperature Sensors
- Beta-coefficient calculation for precise °C reading
- EMA smoothing to reduce noise
-
Non-blocking Loop
- Uses
millis()
-based timing instead ofdelay()
, allowing other tasks to run concurrently
- Uses
-
25 kHz PWM on Pins 9, 10, and 3
- Eliminates annoying audible frequencies
- Implemented via timer register manipulation and a helper function
setPWM_25kHz()
-
Modular Files
Timers.*
: Timer setup for 25 kHz PWMSensor.*
: Beta-NTC reading & exponential smoothingActuator.*
: Base class with hysteresis, separate normal/benchmark curvesPump.*
&Fan.*
: Specializations for pumps (kickstart) and fans
-
Output on 128x64 SSD1306 OLED Display
- Displays temperatures of
sensor1
andsensor2
, PWM Values ofpump1
,pump2
andfan1
,Benchmark Mode (BM)
- Displays temperatures of
- Arduino Nano (5V, ATmega328p).
- 24V PSU !!!! During testing, we found 12V to be a little lacking for our pumps and decided to step it up
- 10 kΩ NTC sensors (Beta ~3950) in voltage divider circuits, analog inputs (
A0
,A1
). - PWM outputs (25 kHz):
- Pump1 → Pin 9 (OC1A)
- Pump2 → Pin 10 (OC1B)
- Fan → Pin 3 (OC2B)
- Benchmark button on Pin 4 (
INPUT_PULLUP
); toggles the second hysteresis and curve set. - Onboard LED (Pin 13) as an indicator for benchmark mode.
- N-Channel MOSFET drivers for pumps and fan.
- SSD1306 128x64 OLED Display (SCL - A5, SDA - A4)
Depending on how beefy your MOSFETs are, you could power all sorts of motors with this setup. The IRLZ44Ns we used in this project are probably overkill but ¯\(ツ)/¯
-
Timer Configuration
initTimers25kHz()
sets Timer1 (Pins 9,10) and Timer2 (Pin 3) to ~25 kHz instead of default Arduino PWM frequencies.
-
Sensor
Sensor
class reads ADC, converts to Celsius via the Beta equation and smooths the result with exponential moving average.
-
Actuator
Base Class- Manages two sets of hysteresis (normal/benchmark) and two temperature->PWM curves.
update(bool benchmarkMode)
checks if the actuator should switch on or off, maps temperature linearly to PWM, then callspostProcessPWM()
.
-
Pump
(Inherits fromActuator
)- Implements a kickstart in
postProcessPWM()
, forcing a high PWM for a set duration to ensure the pump starts spinning reliably.
- Implements a kickstart in
-
Fan
(Inherits fromActuator
)- Shares the same base hysteresis/curve mechanism but typically has no kickstart.
-
Benchmark Mode
- Toggled by a button.
- Uses different on/off thresholds and steeper PWM curves (e.g., higher maximum PWM).
- LED on Pin 13 indicates whether benchmark mode is active.
-
Non-blocking Loop
- The main
loop()
usesmillis()
checks to callupdate()
for sensors and actuators at fixed intervals (e.g., every 500 ms). - No calls to
delay()
are used so the system remains responsive.
- The main
-
Memory Optimization
- Large
float
usage from Beta-equation calls can be replaced with simpler approximations if desired. - Debug strings can be stored in Flash using the
F()
macro to save RAM.
- Large
void loop() {
unsigned long now = millis();
if ((now - lastControlTime) >= controlInterval) {
lastControlTime = now;
handleBenchmarkButton();
if(now - lastDisplayTime >= displayInterval) {
lastDisplayTime = now;
displayMgr.update(); // Schreibt Sensor/Pump/Fan-Werte aufs OLED
}
sensor1.update();
sensor2.update();
pump1.update(benchmarkMode);
pump2.update(benchmarkMode);
fan1.update(benchmarkMode);
}
-
Clone or download this repo with all .h/.cpp files.
-
Open PumpController.ino in Arduino IDE or use PlatformIO.
-
Select "Arduino Nano" and the proper COM port in the IDE.
-
Compile and upload.
-
Wire your hardware as specified (sensors, MOSFET drivers, etc.).
-
Power up and watch the system control the pumps and fan according to the temperatures.
Feel free to open an Issue or Pull Request if you have questions or improvements.
For precise temperature readings, ensure your Beta constant matches your NTC specs.
If you need more available memory, see the notes on disabling log() or simplifying the code.