PWM Fan Controller with Arduino

Hi everyone, I've been in the diy world for quite a bit now and it's time for my first project. I've decided to make a pwm fan controller for my pc that would also have a small lcd. as the atmega328p hasn't got so many pwm outputs and also their frequency i believe is extremely low compared to the 25khz required to control a 4 pin PC fan, i've decided to use an external chip to take care of the pwm signals and ardunio would only need to set the duty cycle. I have initially thought of using multiple 555s, one to make the frequency + one for each fan to control them individually. However the only way have found out to control the % is via a digital potentiometer, and my question is: are there any other ways to change the duty cycle? or, are there more effective/efficient ways to control fans with an arduino with like some i2c or spi ICs? Thanks

PS: if any of you has already done something that gets temperature data sent to arduino, please share it, would be so much helpful

Instead of adding complex external hardware, why not change the PWM frequency.? Or reply #2 of this thread.

How many PWM outputs do you need? Uno has 6, Mega has 14.

There must be a million articles on measuring temperature with Arduino on the net. Google "Arduino measure temperature".

as the atmega328p hasn't got so many pwm outputs

How many fans are you going to control?

and also their frequency i believe is extremely low compared to the 25khz required to control a 4 pin PC fan,

I'm not sure where you got the 25khz requirement. The fan's inertia smooths-out the PWM effects, so it doesn't have to be high frequency.

AC light dimmers & motor speed controls use "phase control" that switches the power on/off at the line frequency of 50 or 60Hz (actually twice the line frequency), something like PWM.

PC brushless fans do have a frequency in the specifications. But they work on pretty much any PWM frequency. Give it a go with your UNO. I think pins 9 and 10 have a higher PWM frequency by default.

I've done this with a Nano and a Pro Mini and I did not even try changing the PWM frequency of the Arduino. It just worked.

For temperatures, I've used a lot of different methods. So long as you don't go below -55 or above 125, then the DS18B20 is the best.

I would need to control 3 - 4 fans individually, and the 25khz frequency is specified in the standard for pc fans. would Arduino be capable of reaching such a high frequency? i know i'll need to change some kind of multiplier for the internal timers. and if I used those 6 pins for PWM, will I have still have enough pins to control 4 1 digit displays and a rotary encoder and 2/3 buttons?

by "temperature data sent to arduino" I meant getting from the PC data for the CPU and GPU temperature, sorry if I wasn't clear enough. I've read that it would be possible through the serial interface and with some VB.net coding, but if anyone has any idea, he would save me some work

Thanks

It’s not necessary to match the frequency. Most fans will tolerate the Arduino’s PWM frequency.

4 individual fans for one CPU? That sounds like fun.

I have no idea how to get the data out of the motherboard. Maybe you will have to read the PWM emitted by the motherboard fan connector(s).

It’s not 4 fans for the CPU, only one, the other three are case fans

anyone can help?

It helps us if you re-state your question. You have had some questions answered above. You may not be happy with those answers or maybe you have more questions to ask now you know a bit more.

GhostRep: I would need to control 3 - 4 fans individually, and the 25khz frequency is specified in the standard for pc fans. would Arduino be capable of reaching such a high frequency? i know i'll need to change some kind of multiplier for the internal timers.

A quick Google search on "Arduino PWM maximum frequency" gave me this excellent answer as very first result.

and if I used those 6 pins for PWM, will I have still have enough pins to control 4 1 digit displays and a rotary encoder and 2/3 buttons?

Depends on how many pins your Arduino has, and the specific other hardware. Can they share pins, for example?

thanks for that answer, it made everything about pwms and timers much clearer to me. talking about pins, i will need at least 4 pins for the pwms to control 4 fans individually, and then I'll be connecting one rotary encoder and 1/2 buttons, and also 1 display (one of the little oleds with 128x64 res or the lcd with 16x2 characters) and if possible also 4 7 segments led displays. Will there be enough pins for everything? I will be using multiplexers and/or shift registers if needed to save some pins. Thanks for your time

ok so I’ve been working all day and I’ve finally managed to get some results.

int btt = 0;
int att = 0;
int dc = 7;
int top = 0;
int perc = 0;

void setup(){
  pinMode(4, INPUT);
  pinMode(2, OUTPUT);
  pinMode(6, OUTPUT);
  
  cli();
  TCCR2A = TCCR2B = OCR2A = TIMSK2 = OCR2B = TCNT2 = 0;
  DDRD |= (1 << 3);
  TCCR2A = 0X23;
  TCCR2B = 0X0B;
  OCR2A = top = 0x13;
  TIMSK2 = 0X02;
  OCR2B = 0x07;
  //sei();
  Serial.begin(115200);
  Serial.println("Ready");
  digitalWrite(6, HIGH);
}

void loop(){  
btt = digitalRead(4);
      if (btt == 1 && att == 0 && dc < 19)  {
        dc = dc + 1;
        OCR2B = dc;
        att = 1;
        digitalWrite(2, HIGH);
     }
      if (btt == 1 && att == 0 && dc == 19)  {
        dc = 7;
        OCR2B = dc;
        att = 1;
        digitalWrite(2, HIGH);
     }
      if (btt == 0 && att == 1)  {
        att = 0;
        perc = dc*100/top;
        digitalWrite(2, LOW);
        Serial.println(top, DEC);
        Serial.println(dc, DEC);
        Serial.print(perc, DEC);
        Serial.println("%");
      }
}

with this I can easily change the duty cycle with 5-6% stepping from around 35 up to 100 and it works like a charm. the problem is about the serial connection. it seems like there is some kind of buffer that gets filled first and then data gets sent to the PC. For example for the Ready message in setup only “Re” gets sent. After a few button presses, I get the rest of the word and some prints about top dc and perc coming from much earlier button presses. I thought it was a problem with interrupts so I’ve both tried to remove cli() and sei() but it just breaks the whole thing and nothing then works.
furthermore, is there a quick way to check the pwm frequency coming out of the arduino without an oscilloscope?

Thanks

The code as posted doesn't turn interrupts back on. Serial is not supposed to work without interrupts. The only reason that it does work is a hack in the Arduino core which will detect this and try to work around it.

You're allowed to use more than 3 letters in your variable names. Use a longer description of what each one contains. It makes it so much easier to read and there's no penalty except a bit more typing.

You can use a frequency-measuring library on your Arduino to measure the output. Either use a second Arduino or feed the output back into another pin. If you use feedback, check that your use of the timer doesn't conflict with the library.

int button = 0;
int alreadypressed = 0;
int dutycycle = 7;
int top = 0;
int percentage = 0;

void setup(){
  pinMode(4, INPUT); //button for toggling pwm duty cycle
  pinMode(2, OUTPUT); //LED to acnkowledge button pressed
  pinMode(6, OUTPUT); //LED to acnkowledge setup phase complete
  
  cli();
  TCCR2A = TCCR2B = OCR2A = TIMSK2 = OCR2B = TCNT2 = 0;
  DDRD |= (1 << 3);
  TCCR2A = 0X23;
  TCCR2B = 0X0B;
  OCR2A = top = 0x13;
  TIMSK2 = 0X02;
  OCR2B = 0x07;
  sei();
  Serial.begin(115200);
  Serial.println("Ready");
  digitalWrite(6, HIGH);
}

void loop(){  
button = digitalRead(4);
      if (button == 1 && alreadypressed == 0 && dutycycle < 19)  {
        dutycycle = dutycycle + 1;
        OCR2B = dutycycle;
        alreadypressed = 1;
        digitalWrite(2, HIGH);
     }
      if (button == 1 && alreadypressed == 0 && dutycycle == 19)  {
        dutycycle = 7;
        OCR2B = dutycycle;
        alreadypressed = 1;
        digitalWrite(2, HIGH);
     }
      if (button == 0 && alreadypressed == 1)  {
        alreadypressed = 0;
        percentage = dutycycle*100/top;
        digitalWrite(2, LOW);
        Serial.println(top, DEC);
        Serial.println(dutycycle, DEC);
        Serial.print(percentage, DEC);
        Serial.println("%");
      }
}

So I have rewritten the code with longer variables’ names so that it is easier to read.
I was said that Serial connections do need interrupts, but enabling them won’t affect the rest of the code as it changes internal registers etc.?
anyway, I also kept on with the “having a separate circuit to handle the pwm” track, and I built a digital circuit in Logisim: it is made of a 4-bit counter (16 steps are fine for PWM – around 6% stepping) and its output is constantly compared with 4 single bits coming from Arduino (or a register or something like that) toggling the state of the output and therefore the PWM signal at the time needed to simulate the PWM duty cycle.

What do you think about it? would it be possible to actually make it?
I would use the same counter for all the fan channels, with the only thing varying is the value in the comparing register.
Thanks

GhostRep: So I have rewritten the code with longer variables' names so that it is easier to read.

Except for the pins and the magic number "19". You still haven't turned interrupts back on.

sorry, I have corrected