Project tutorial
Control PWM Fans with an Arduino

Control PWM Fans with an Arduino © GPL3+

Control PWM fans with an Arduino, some basic C# programming and a lot of fun!

  • 6,323 views
  • 1 comment
  • 12 respects

Components and supplies

A000066 iso both
Arduino UNO & Genuino UNO
Any arduino will probably work, just needs an PWM header.
×1

About this project

Background

So I searched around the internet to find a project or an application that could read the CPU temp on my PC and with the help of an Arduino change the speed of my PWM fans inside of the case. I couldn't find anything complete so I combined all of the short code snippets to a working program. I'm new to programming in both C# and Arduino so the code is a bit messy and I do not have the motivation right now to clean it up.

How it works

The system works by an application running on the PC checking the CPU temperature and checking whether to speed up or slow down the fans. The PC communicates with the arduino via the serial port.

How to connect it

Firstly, make sure that you fan/s are PWM controller, you can check this by making sure that your fan connector has 4 wires. If they are PWM, take a small flat-headed screwdriver and pull the PWM pin out of the fan connector by pushing the metal part on top and pulling it back.

Then solder/crimp/twist another wire to that and connect it to the arduinos PWM 3 header. Then you're done, I've connected 3 fans to the same header and it works fine so far.

Once this is done you're ready to connect your arduino to the PC and fire up the programs.

Code

Arduino CodeArduino
This is the code that recives the fan speed updates and acts accordingly.
/*
 * Thanks to https://www.youtube.com/watch?v=vHeG3Gt6STE for the tutorial
 * Edited by Simon Granstrm
 */

word VentPin = 3;
String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
String commandString = "";

boolean isConnected = false;

byte lastFanSpeed = 0;

void setup() {
  pinMode(VentPin, OUTPUT);
  Serial.begin(9600);
  pwm25kHzBegin();
  pwmDuty(30);
  /*
  inputString = "#UPDATE45\n";
  stringComplete = true;
  */
}

void loop() {
  CheckForSerial();
  
  if(stringComplete)
  {
    stringComplete = false;
    getCommand();
    
    if(commandString.equals("UPDATE"))
    {
      UpdateFanSpeed();
    }
    
    inputString = "";
  }
}

void pwm25kHzBegin() {
  TCCR2A = 0;                               // TC2 Control Register A
  TCCR2B = 0;                               // TC2 Control Register B
  TIMSK2 = 0;                               // TC2 Interrupt Mask Register
  TIFR2 = 0;                                // TC2 Interrupt Flag Register
  TCCR2A |= (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);  // OC2B cleared/set on match when up/down counting, fast PWM
  TCCR2B |= (1 << WGM22) | (1 << CS21);     // prescaler 8
  OCR2A = 79;                               // TOP overflow value (Hz)
  OCR2B = 0;
}

void pwmDuty(byte ocrb) {
  OCR2B = ocrb;                             // PWM Width (duty)
}

void getCommand()
{
  if(inputString.length()>0)
  {
     commandString = inputString.substring(1,7);
  }
}

void UpdateFanSpeed() {
  int newFanSpeed = (inputString.substring(7,inputString.length())).toInt();
  
  if (newFanSpeed != lastFanSpeed) {
    pwmDuty((byte)(newFanSpeed / (100 / 79)));
    lastFanSpeed = newFanSpeed;
  }
}

void CheckForSerial() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}
C# ProgramC#
This is the program that reads and sends updates to the arduino. Some code is unnecessary but I don't have the motivation to clean it up right now.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OpenHardwareMonitor.Hardware;

namespace ArduinoPWMController
{
    public class Program
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        const int SW_HIDE = 0;
        const int SW_SHOW = 5;

        static void Main(string[] args)
        {
            /*
             * Cred to : https://stackoverflow.com/a/3571628 for the console hide function.
             */
            var handle = GetConsoleWindow();

            // Hide or Show console
            ShowWindow(handle, SW_HIDE);

            Controller controller = new Controller();

            while (true)
            {
                float cpuTemp = CpuTemperature.CpuTemp.GetCpuTemp();
                Console.WriteLine("Current temp: " + cpuTemp.ToString());

                if (cpuTemp < 50) //Under 40 C
                {
                    controller.UpdateFanSpeed(35);
                }
                else if (cpuTemp >= 50 && cpuTemp < 57) //Between 50 and 57 C
                {
                    controller.UpdateFanSpeed(45);
                }
                else if (cpuTemp >= 57 && cpuTemp < 62) //Between 57 and 62C
                {
                    controller.UpdateFanSpeed(55);
                }
                else //Above 62 C
                {
                    controller.UpdateFanSpeed(100);
                }

                System.Threading.Thread.Sleep(5000);
            }
        }
    }

    /*
     * Credit for the base controller class https://www.youtube.com/watch?v=vHeG3Gt6STE
     * Edited by: Simon Granstrm
     */

    public class Controller
    {
        bool isConnected = false;
        string selectedPort;
        String[] ports;
        SerialPort port;

        public Controller()
        {
            getAvailableComPorts();

            /*foreach (string port in ports)
            {
                Console.WriteLine(port);
            }
            Console.WriteLine("");

            Console.Write("Choose a port: ");

            selectedPort = Console.ReadLine();

            Console.WriteLine("");*/

            selectedPort = "COM3";

            connectToArduino();
        }

        private void getAvailableComPorts()
        {
            ports = SerialPort.GetPortNames();
        }

        private void connectToArduino()
        {
            isConnected = true;
            port = new SerialPort(selectedPort, 9600, Parity.None, 8, StopBits.One);
            port.Open();
        }

        private void disconnectFromArduino()
        {
            isConnected = false;
            port.Close();
        }

        public void UpdateFanSpeed(int _percentage)
        {
            //float arduinoValue = _percentage / (100f / 79f);

            //Console.WriteLine("Sending: " + arduinoValue.ToString());

            string _send = "#UPDATE" + _percentage.ToString() + "\n";
            port.Write(_send);
            Console.WriteLine("Sent: " + _send);
        }
    }
}

/*
 * Major thanks to https://openhardwaremonitor.org/downloads/.
 * Also thanks to  https://www.hackster.io/haoming-weng/get-cpu-temperature-using-open-hardware-monitor-in-c-1a3338 for the instructions.
 */

namespace CpuTemperature
{
    class CpuTemp
    {
        public class UpdateVisitor : IVisitor
        {
            public void VisitComputer(IComputer computer)
            {
                computer.Traverse(this);
            }
            public void VisitHardware(IHardware hardware)
            {
                hardware.Update();
                foreach (IHardware subHardware in hardware.SubHardware) subHardware.Accept(this);
            }
            public void VisitSensor(ISensor sensor) { }
            public void VisitParameter(IParameter parameter) { }
        }

        public static float GetCpuTemp()
        {
            UpdateVisitor updateVisitor = new UpdateVisitor();
            Computer computer = new Computer();
            computer.Open();
            computer.CPUEnabled = true;
            computer.Accept(updateVisitor);

            float combinedTemp = 0;
            int numberOfSensors = 0;

            for (int i = 0; i < computer.Hardware.Length; i++)
            {
                if (computer.Hardware[i].HardwareType == HardwareType.CPU)
                {
                    for (int j = 0; j < computer.Hardware[i].Sensors.Length; j++)
                    {
                        if (computer.Hardware[i].Sensors[j].SensorType == SensorType.Temperature)
                        {
                            combinedTemp += (float)computer.Hardware[i].Sensors[j].Value;
                            numberOfSensors++;
                        }
                    }
                }
            }

            float avgTemp = combinedTemp / numberOfSensors;

            computer.Close();

            return avgTemp;
        }
    }
}

Comments

Similar projects you might like

Control an LED with the Remote Control

Project showcase by Nicholas_N

  • 10,847 views
  • 5 comments
  • 16 respects

25 kHz 4 Pin PWM Fan Control with Arduino Uno

Project tutorial by xXarduino_11Xx

  • 1,350 views
  • 0 comments
  • 9 respects

Servo Control with TV Remote Control

Project showcase by eldo85

  • 11,819 views
  • 5 comments
  • 33 respects

Control Your Computer With A Remote Control

Project tutorial by Arduino_Scuola

  • 7,737 views
  • 1 comment
  • 9 respects

Control Home Appliances Through Web Or Mobile

Project tutorial by Team Gadget Programmers

  • 10,154 views
  • 4 comments
  • 22 respects
Add projectSign up / Login