4pin fan controller with rpm display using arduino nano

Hey guys this is my first arduino project ever (basically idk anything and am learning from scratch). My laptop overheats a lot so I am making my own cooling pad. I made this circuit to control an Arctic P12 max fan and display the rpm. I made this circuit by referring to [Hardware example (Arduino)]. (Hardware example (Arduino))

However my rpm readings are off the charts (100000) and the pulses I get from the tach are in the range of (10000-100000) when it should be around 130 at max. Idk where I am going wrong. Please advice.

This is the code I am using rn just for diagnostics.

#include <U8g2lib.h>
#define TACH_PIN 2
#define PWM_PIN 9   // Controls fan speed
#define POT_PIN A0  // Potentiometer input

volatile unsigned long pulseCount = 0;

// Create the U8g2 object for SH1106 OLED (assuming you're using I2C and the U8G2_SH1106_128X64_NONAME_F_SW_I2C constructor)
U8G2_SH1106_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* SCL=*/ A5, /* SDA=*/ A4, /* RST=*/ U8X8_PIN_NONE);

void countTachPulse() {
  pulseCount++;
}

void setup() {
  Serial.begin(9600);
  pinMode(TACH_PIN, INPUT_PULLUP);
  pinMode(PWM_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(TACH_PIN), countTachPulse, FALLING);

  u8g2.begin();  // Initialize OLED
}

void loop() {
  // 1. Set fan speed from potentiometer
  int potVal = analogRead(POT_PIN);
  int pwmVal = map(potVal, 0, 1023, 0, 255);
  analogWrite(PWM_PIN, pwmVal);

  // 2. Read tach pulses
  pulseCount = 0;
  delay(1000);
  unsigned long rpm = (pulseCount * 60UL) / 2;  // 2 pulses/rev for most fans

  // 3. Print results to Serial Monitor
  Serial.print("Potentiometer: "); Serial.print(potVal);
  Serial.print(" | PWM: "); Serial.print(pwmVal);
  Serial.print(" | Pulses: "); Serial.print(pulseCount);
  Serial.print(" | RPM: "); Serial.println(rpm);

  // 4. Display results on OLED
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x10_tr);  // Compact font for better fitting

  u8g2.setCursor(0, 12);
  u8g2.print("Pot: "); u8g2.print(potVal);

  u8g2.setCursor(0, 24);
  u8g2.print("PWM: "); u8g2.print(pwmVal);

  u8g2.setCursor(0, 36);
  u8g2.print("Pulses: "); u8g2.print(pulseCount);

  u8g2.setCursor(0, 48);
  u8g2.print("RPM: "); u8g2.print(rpm);

  u8g2.sendBuffer();
  // Send the buffer to the OLED for display

  delay(100);  // Short delay before the next loop
}

some sample outputs are:
Potentiometer: 181 | PWM: 45 | Pulses: 9478 | RPM: 284340
Potentiometer: 179 | PWM: 44 | Pulses: 9693 | RPM: 290790
Potentiometer: 178 | PWM: 44 | Pulses: 10057 | RPM: 301710
and when Pot is 0 and pwm is 0 the pulses are around 3000 and rpm around 80k

Please Advice!!

Try

  // 2. Read tach pulses
  noInterrupts();
  pulseCount = 0;
  interrupts();

  delay(1000);
  noInterrupts();
  unsigned long rpm = (pulseCount * 60UL) / 2;  // 2 pulses/rev for most fans
  interrrupts();

nope still gives me crazy pulse and rpm values.
I was reading through: [How to properly control PWM fans with Arduino - Federico Dossena] (How to properly control PWM fans with Arduino - Federico Dossena) and https://www.baldengineer.com/pwm-3-pin-pc-fan-arduino.html

I don't quite get what it means but I think the issue is related

if you let it run for a while in the same setting of the potMeter.

Does the RPM goes up or does it stay roughly the same?

Have you checked this for your fan?

stays roughly the same.
if i change the setting it does decrease or increase but the numbers are huge

yup i did. didn't work

It might be noise from the fan that is interfering.
Add a 1K resistor between +5V and the input pin could reduce it

if you're talking about pull up resistance, i've done that using the 12v line and 10k resistors. this result is after that. before the pull up resistors it was way worse, i was getting negative rpms

datasheet doesn't tell much

for now i'll try to just calculate rpm based on my pwm value but I want to get rpm from tach pin later

As your RPM is expected between 400-3300 RPM (datasheet) the number of pulses per second is between 12 and 110.
So the pulses can be no closer than about 9 milliseconds apart.

Adding debounce handling into the interrupt handling

void countTachPulse() 
{
  static uint32_t lastTime = 0;
  uint32_t now = micros();
  if (now - lastTime > 9000)  //  <<<<<<<<<  can be tuned
  {
    pulseCount++;
    lastTime = now;
  }
}

Your voltage divider for D2 is wrong. It is only providing 2.8V, You need at least 3.5V.

Connect the tach output to a 10K resistor to 5V and connect the tach to D2

Also GNDM MUST be connected to GND

From unsigned long rpm??

nah that was another iteeration of the code

i tried that and even measured with multimeter but it wasnt reaching either of those values. it was around 0.3v at d2. could you give me the circuit diagram. im new to this maybe i did something wrong

i've tried that but it gives inaccurate rpm. like 2700 when pwm is 0

Very simple:


Idk if i'm correct but is that how its done?
red wire is 5v
green is tach
and orange is going to d2

i connected gnd and gndm when you pointed out last time. also just for info what does that do though? I didn't notice any difference.

rn with the 5v thing the pulses dropped to about 500-5000 range

Idk if i'm correct but is that how its done?

Looks right

All signals need a return path and that is usually the ground. The GNDM from the fan need to be connected to the GND of the Arduino or the circuit will not be complete.

Well now that the hardware is correct, then the software need some fixing.