MyName1sSimon
Published © GPL3+

Control PWM Fans with an Arduino

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

BeginnerFull instructions provided11,705
Control PWM Fans with an Arduino

Things used in this project

Hardware components

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

Story

Read more

Code

Arduino Code

Arduino
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# Program

C#
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;
        }
    }
}

Credits

MyName1sSimon

MyName1sSimon

0 projects • 0 followers

Comments