12V, 4 Wire Fan Controller With Arduino Uno

by Jimboborino in Circuits > Arduino

32 Views, 1 Favorites, 0 Comments

12V, 4 Wire Fan Controller With Arduino Uno

Fan Controller.jpg

Speed controller for 4 wire 12V fan for 3D printer enclosure.

There is surprisingly a lot to this project, and it's also a learning experience. I'll break this project down and explain the steps as we go where I can. In the end, You should have a good understanding of what's going on, and hopefully be able to build on it and add more features yourself!


I needed a fan to maintain the temperature of my 3D printer enclosure. The enclosure has an opening for a 120mm fan. So I made a speed controller that controlled the speed based on the temperature read by the temperature sensor. Since I have lots of displays laying around from other projects, I figured I would add that in too.


I extensively referred to a couple of sources for the coding on this project as I was figuring it out. You'll see the DNA of their code in the final steps.

https://www.instructables.com/Standalone-Arduino-Altimeter/

and

https://fdossena.com/?p=ArduinoFanControl/i.md

Supplies

Arduino Uno

12V power supply

5V power supply

BME280 temperature and humidity sensor

UCTRONICS 0.96 Inch OLED Module 12864 128x64 Yellow Blue SSD1306

Noctua NF-F12 PWM fan. (any 12V, 4wire, 5V PWM fan should work. I wanted a quiet one.)

Solderless breadboard (highly recommended)

Jumper wires

Connecting the Components

BME.png

Using your jumper wires and preferably, a breadboard, let's connect the BME280 to your Arduino.

A4 and A5 are the default I2C connections on the Uno. If you're using a different microcontroller, then you will need to adjust accordingly.

We'll only need to connect 4 wires to make the magic happen.

The BME280 can take 5V as an input, but I used the 3.3V just in case.

Arduino ---- BME280

3.3 ----------- VCC

GND ---------GND

A4 ------------ SDA

A5 ------------- SCL


SCL and SDA are the data lines for I2C communication; Serial CLock and Serial DAta. That's really all there is to it. For a quick and dirty hobby project, there's not much to know, but this communication protocol is quite common, and I highly recommend reading more about it. Most every hobby level and even professional device has I2C communication, and once you understand it, you'll be able to make bigger, more complex projects that do a lot of cool things.

Sensing the Sensor

with serial monitor.jpg

I'm assuming you know how to set up an Arduino on your computer.

If not, please visit https://www.arduino.cc/en/Guide/ArduinoUno/


Open your Arduino IDE and go to Libraries.

You'll want to add the library BME280_Arduino_I2C

It can also be found here https://github.com/paramoshkinandrew/BME280_Arduino_I2C


Then, you can upload the BME_280_I2C sketch (below) to the arduino (it may be in your list of examples too)


Opening your serial monitor of the IDE, you can see the data coming into your screen!

If not, check your wiring, power, and ground.

You should have a repeating cycle of

> Temperature (C): 26.31
> Humidity (%): 53
> Pressure (Pa): 101374.75

on the serial monitor. Your results may vary based on your location.


The code in this one is pretty easy. The setup portion initializes the BME, and the loop portion takes a reading. If the reading is anything other than null, it sends the data to the serial monitor. Boom. Done!


Now let's move on to the display!

Downloads

Displaying the Display

BME and display.png

The SSD 1306 OLED display is also I2C. So, it can be run in parallel to the BME sensor. This is part of why I2C is so useful!

I find it easiest to wire the display connections to the corresponding connection on the BME.

I try to unplug the Arduino when I connect anything. Try. So far I have only ever fried 2 Arduinos, so they are pretty robust.

BME-----Display

VCC----VCC

GND----GND

SCL----SCL

SDA----SDA

Super easy; barely an inconvenience.


Now we need to add another library in the library section of your IDE

U8glib.h

It has been superseded, but I wrote this code with U8glib.h, so....

Once that is done, load up the Temp_Humid_sensor.ino sketch (below)


You will see the line (around Line 8 in the IDE)

char Printername[15] = "Your Printer";

As this is a project designed for a 3D printer enclosure, that name will display on the top of your display. You can change it to whatever you want, just keep it under 15 characters.


I added comments to the code as much as possible. Each one explains what the corresponding code does. Most of it is rather easy to figure out. A couple of lines are not. To me.

Once it's loaded you should see something like the image below. Your results may vary based on your position on earth.

Feeling the Fan

With Fan.png

This one is going to be a bit tricky. Unfortunately, it does not use I2C.

It's a 12V fan, but the control line is a 5V PWM signal.

We'll start with the sketch this time.

Upload the below sketch Temp_humid_Fan_add_anim.ino

Once it is in the arduino, unplug it, and turn off the power supply.


Let's connect the 12V power supply to the power input of your fan (make sure you check for your specific fan what pins are what).

Then we connect the ground of the 12V power supply to the ground of the arduino and the ground of the fan. This makes sure that the 5V and 12V are referencing the same ground.

Next, we connect the PWM output, pin D9 to the PWM line on the fan.

Finally, the tach output from the fan goes to pin D2 of the arduino.

12V--Fan Power

GND--Fan GND--Arduino GND

Arduino D9--Fan PWM input

Fan Tach out -- Arduino D2


Check your wiring multiple times on this one. Things go bad quickly when you get above 5V with these kinds of projects. (I once had 28V get on to my serial lines of my arduino and back to my laptop. It did not end well!)



Things get pretty thick in the sketch. We still have the BME in there, you can find those lines rather easily.

float T, H; //Temp and humidity. Guess which one is which!

BME280Data* data = bme.read(); //This is where the measurement happens

if (data != nullptr) { //If it comes back with anything other than null, it will start assiging data

T = (data->temperature); //Making T the data for temp
H = (data->humidity); //take a wild guess


The display loop has gotten much bigger!

u8g.setFont(u8g_font_ncenB12r); //Sets the font for the yellow title part of the screen
u8g.drawStr( 0, 12, Printername); //Writes your printer name

dtostrf(T, 4, 1, sT); //Changes float T to string T
dtostrf(H, 2, 0, sH); //Same for H
//dtostrf(cR, 4, 4, sR);

u8g.setFont(u8g_font_ncenB14r); //Font for the C and %
u8g.drawStr( 50, 35, "C");
u8g.drawStr( 102, 35, "%");

u8g.setFont(u8g_font_ncenB08r);
u8g.drawStr( 118, 25, "R");
u8g.drawStr( 118, 35, "H");

u8g.setFont(u8g_font_ncenB18r); //Font for Temp and humidity
u8g.drawStr( 0, 35, sT); //Displaying the Temp
u8g.drawStr( 72, 35, sH); //And the humidity


And we have a LOT going on to control the fan.

void setupTimer1(){
//Set PWM frequency to about 25khz on pins 9,10 (timer 1 mode 10, no prescale, count to 320)
TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
TCCR1B = (1 << CS10) | (1 << WGM13);
ICR1 = 320;
OCR1A = 0;
OCR1B = 0;
}

//equivalent of analogWrite on pin 9
void setPWM1A(float f){
f=f<0?0:f>1?1:f;
OCR1A = (uint16_t)(320*f);
}

This section is used just to change the PWM output to 25KHz, which the fan needs in order to operate correctly. The Arduino has a default speed of 490hz.


We have also added 2 bitmaps encoded into C for a little fan spinning animation.

const unsigned char epd_bitmap_fanblades3i [] PROGMEM = {
0x00, 0x3e, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0xc0, 0x03, 0xff, 0xc0, 0x03,
0xff, 0xc0, 0x03, 0xff, 0xc0, 0x01, 0xff, 0xc0, 0x00, 0xff, 0x80, 0x00, 0x3f, 0x80, 0x00, 0x1f,
0x00, 0x0f, 0x1e, 0x18, 0x3f, 0xfe, 0x7c, 0x7f, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xc7, 0xff, 0xff, 0xc7, 0xff, 0x7f, 0xc7, 0xff, 0x7f, 0xc7, 0xfe, 0x3f, 0xc7, 0xfe, 0x3f,
0xc3, 0xfc, 0x1f, 0x81, 0xf8, 0x07, 0x00, 0x70
};
// 'fanblades4i', 24x24px flipped image
const unsigned char epd_bitmap_fanblades4i [] PROGMEM = {
0x0e, 0x00, 0xe0, 0x1f, 0x81, 0xf8, 0x3f, 0xc3, 0xfc, 0x7f, 0xe3, 0xfc, 0x7f, 0xe3, 0xfe, 0xff,
0xe3, 0xfe, 0xff, 0xe3, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff,
0xfe, 0x3e, 0x7f, 0xfc, 0x18, 0x78, 0xf0, 0x00, 0xf8, 0x00, 0x01, 0xfc, 0x00, 0x01, 0xff, 0x00,
0x03, 0xff, 0x80, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xc0, 0x03, 0xff, 0xc0, 0x03, 0xff, 0x80, 0x01,
0xff, 0x80, 0x00, 0xff, 0x00, 0x00, 0x7c, 0x00
};


And this bit to read the tach output using one of the interrupt pins

unsigned long calcRPM(){
if(millis()-ts2<FANSTUCK_THRESHOLD&&ts2!=0){
return (60000/(ts2-ts1))/2;
}else return 0;
}


There's more going on too, but this is becoming a dissertation at this point!


What you should see when you power your Arduino up is a display giving you the printer name at the top in yellow, the temperature and humidity in the middle, and the fan RPMs on the bottom, with a spinning fan icon.

Your fan should also be spinning. If you're on the cool end, it'll be rather slow. Warm: fast.

To check if it's working, you can breathe on the sensor and warm it up. The fan should start spinning faster, and the tach display should increase. Bringing something next to the sensor to cool it down will produce the opposite effects. I don't recommend ice. I have a large metal plate near an AC unit, so I used that near the sensor to cool down the surrounding air.


Now you have your own fan speed controller based on temperature, with the added benefit of humidity sensing too!