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 provided28,798
Control PWM Fans with an Arduino

Things used in this project

Hardware components

Arduino UNO
Arduino 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