Problem with Temperature PID, PWM, 25Khz, 4-Pin Fan Control and RPM reading

Hey guys, I’m very noob, but based on a bunch of projects and topics related here I managed somehow to make this almost work.

Project:
The purpose of the project is to control a 4-pin Fan (INTEL D34017-002 by DELTA) speed trough the PWM (25Khz), using an Adaptive Tuning PID based on a LM35 temperature sensor values, and read the RPM. So far things are going fine, I can read the RPM, and temperatures, and there is a variation of the PWM output, but it seems to be not very precise or efficient for temperature control somehow (Despite PID parameters issue).

The PROBLEM:
I can’t manage to achieve a proper control of the Fan Speed, I can only have it as a High about 2800RPM or Low about 1200RPM and not a linear transition between those values. How can I improve it??

Also, any suggestions on making a variable heating source? so far I have been using a 60W Light bulb, but I want to use kind of a resistor and control it also using a PID.

PS.:
#There is a need of study for the Ki, Kp and Kd parameters setting (I used those randomly).

T1 is only for room temperature reading.

#I have limited resources.
#I did not put it here, but on my paper I give proper references to all the projects and people I’m based on.
#My main contribution will be in the heat transfer prediction and simulation, so if there is a simpler way to make this PID control efficiently it is ok.

CODE:

/********************************************************
Code to Control the speed (and reed RPM) of INTEL D34017-002 da DELTA, by 25Khz PWM,
Using Adaptative Tunings PID from LM35 Temperature Reading. 
 ********************************************************/
 
//PID Library
#include <PID_v1.h>


//Define oinputs and output pins

#define PIN_INPUT 0
#define PIN_INPUT1 1
#define PIN_OUTPUT 3


//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Define the aggressive and conservative Tuning Parameters

double aggKp=4, aggKi=0.2, aggKd=1;
double consKp=1, consKi=0.05, consKd=0.25;

// Scale, and temperature related parameters
double S1 = 0;
double T1 = 0;
double S2 = 0;
double T2 = 0;
double Scale = 0;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, REVERSE);
 
 //RPM reading parameters.

 volatile byte half_revolutions;

 unsigned int rpm;

 unsigned long timeold;


      /********************************************************
        Início do SETUP;
      ********************************************************/

      void setup()
      {
        
      Serial.begin(9600); //
      TCCR2A = 0x23;   // 
      TCCR2B = 0x09;  // Select clock
      OCR2A = 79;   // Aiming for 25Khz
      
      pinMode(PIN_OUTPUT, OUTPUT);  // Enable the PWM output (you now have a PWM signal on digital pin 3)
        Input = (T1);
        Setpoint = 75;

      //turn the PID on
        myPID.SetMode(AUTOMATIC);
      
      //RPM
      attachInterrupt(0, rpm_fun, RISING);
      
      half_revolutions = 0;
      rpm = 0;
      timeold = 0;
      }
      
      
      /********************************************************
        LOOP;
      ********************************************************/

      void loop()
      {
        S1 = analogRead(PIN_INPUT);//Read First LM35
        T1 = (S1 * 500)/1023;//LM35 Conversion Temperature
      
        S2 = analogRead(PIN_INPUT1);//Read Second LM35
        T2 = (S2 * 500)/1023;///LM35 Conversion Temperature
        Input = (T2);
      
          /********************************************************
             RPM:
          ********************************************************/
        if (half_revolutions >= 40) { 


           rpm = 30*1000/(millis() - timeold)*half_revolutions;//RPM
           timeold = millis();
           half_revolutions = 0;
           Serial.print("RPM: ");
           Serial.println(rpm,DEC);
           digitalWrite(2, HIGH);//Set internal pull-up, so u don't need a 10K Resistor
         } 
      
       /********************************************************
        Printing
       ********************************************************/
      
        Serial.print("T1: ");
        Serial.print(T1);//Imprime o valor de T1 (temperatura)
        Serial.println(" C");//imprime em seguida
        Serial.print("T2: ");
        Serial.print(T2);//Imprime o valor de T1 (temperatura)
        Serial.println(" C");//imprime em seguida
        Serial.print("           Control: ");
        Serial.print(Scale*0.833);//imprime em seguida
        Serial.println(" %");//imprime em seguida
        Serial.print("                            ");
      /********************************************************
        PID Agressive and Conservative definition
      ********************************************************/
        
        double gap = abs(Setpoint-Input); //distance away from setpoint
        if (gap < 5)
        {   //we're close to setpoint, use conservative tuning parameters
          myPID.SetTunings(consKp, consKi, consKd);
        }
        else
        {
          //we're far from setpoint, use aggressive tuning parameters
           myPID.SetTunings(aggKp, aggKi, aggKd);
        }
      
      /********************************************************
        PID

      ********************************************************/
      
        myPID.Compute();
        Scale = (Output*0.3098039); //Usa escala para que a saída seja compatível com o ciclo de trabalho escolhido
        analogWrite(PIN_OUTPUT, Scale);//Define a saída e impõe o valor de 'Scale' como saida PWM.
        delay(1000);//Intervalo de cada ciclo
      }

/********************************************************
  RPM Function
 ********************************************************/

 void rpm_fun()
 {
   half_revolutions++;
   //a cada rotacão, ocorre duas interrucoes
 }

but it seems to be not very precise or efficient

For starters, precise and efficient are terms that might apply to completely different parts of the project. For instance, precise might apply to the values you get from the temperature sensor. Except that, in the case of the LM35, it does not. The LM35 is not a highly accurate device.

        delay(1000);//Intervalo de cada ciclo

Reading the temperature once a second, sticking your head in the sand the rest of the time, is NOT efficient. There is no reason not to make use of your less-than-accurate data more often.

           digitalWrite(2, HIGH);//Set internal pull-up, so u don't need a 10K Resistor

The internal pullup resistor does not turn itself off at random intervals. You do not need to keep turning it on.

        myPID.Compute();
        Scale = (Output*0.3098039); //Usa escala para que a saída seja compatível com o ciclo de trabalho escolhido
        analogWrite(PIN_OUTPUT, Scale);//Define a saída e impõe o valor de 'Scale' como saida PWM.

You print useless information, like T1, but not useful information, like Output or Scale. I'm sure that there is a good reason for that, but I can't imagine what it is.

I can't manage to achieve a proper control of the Fan Speed, I can only have it as a High about 2800RPM or Low about 1200RPM and not a linear transition between those values.

You should be testing this independent of the code to use the fan. If the fan/wiring/power supply is inadequate/defective, it makes no sense developing a bunch of code to use the defective fan.

You have not set up Timer2 correctly for 25KHz.

For a top value of 80 counts (OCR2A = 79) you need a prescaler of 8 for a 40 us period with a 16MHz clock. (8 x .0625 = 4us/tick) With TCCR2B = 0x09 you are using a prescaler of 1 and actually getting 200KHz.

Furthermore, when you use analogWrite(PIN_OUTPUT, Scale) no scale value over 78 is valid.

Using the hex values to represent the timer register settings is poor programming practice and obscures what you are doing. It is far better to set bit values with explicit reference to the register bit names used in the data sheet.

This is Timer2 set up for 25KHz.

 TCCR2A = 0;//initialize timer registers
 TCCR2B = 0;

  // Set the timer to mode 7... Fast PWM to OCR2A
  // Set output on Channe2B Pin3
  // Clear on compare match
  TCCR2A |= (1 << WGM21) | (1 << WGM20); // part of mode 7 setting
  TCCR2B |= (1 << WGM22); //third bit of mode 7
  TCCR2A |= (1 << COM2B1); //Pin 3, Clear on compare match

  //Set fast pwm to 25 khz
  TCCR2B |= (1 << CS21); //Prescaler 8 .5us per tick
  OCR2A = 79; //80 ticks = 40us = 25khz

Using the hex form, this setup is TCCR2A = 0x23 or B00100011 and TCCR2B = 0x0A or B00001010