Teensy 4 and 4pin fan -> problems with rpm speed

Hello everybody!
I am new here in the forum and my English is unfortunately not the best, sorry! :eek:
I bought the teensy 4 and wanted to port an old project from an Arduino Mega.
Unfortunately I can't get the rotation speed of a 4-pin fan. :confused:
Here are my attempts:

Code:
/*
Lueftersteuerung RadiationShield
- PWM gesteuert bei uber- bzw unterschreiten eingestellter Schwellwerte
*/
#include <Thermistor.h>
#include <NTC_Thermistor.h>
#include <SmoothThermistor.h>
/********************************************/
/*                PIN-Section               */
/*      Hier Sensor-Pins definieren         */
/********************************************/
#define PWM_FAN_PIN_BLUE 4
#define RPM_FAN_PIN_GREEN 7
#define UPDATE_ZYKLUS 1000
#define MAX_SPEED 255
#define MIN_SPEED 50
#define FAN_FLANKEN 4

// Thermistor NTC10k
#define PIN_INPUT              14
#define REFERENCE_RESISTANCE   10000
#define NOMINAL_RESISTANCE     10000
#define NOMINAL_TEMPERATURE    30
#define B_VALUE                3950
/**
  Smoothing factor of a temperature value.
*/
#define SMOOTHING_FACTOR 5
Thermistor* thermistor = NULL;
float celsius;


/********************************************/
/*           Variablen-Section              */
/********************************************/
int speed;
int FanSpeed = 0;
float TempNTC;
int RevTemp = 25;
unsigned long letzte_ausgabe = 0;
volatile int counter_rpm;

/********************************************/
/*              Setup-Section               */
/********************************************/
void setup(){
  Serial.begin(115200);
  Thermistor* originThermistor = new NTC_Thermistor(
    PIN_INPUT,
    REFERENCE_RESISTANCE,
    NOMINAL_RESISTANCE,
    NOMINAL_TEMPERATURE,
    B_VALUE
  );
  thermistor = new SmoothThermistor(originThermistor, SMOOTHING_FACTOR);

  celsius = thermistor->readCelsius();
  
  pinMode(PWM_FAN_PIN_BLUE, OUTPUT);
  speed=MIN_SPEED; //Langsam beginnen


  pinMode(RPM_FAN_PIN_GREEN, INPUT);
  digitalWrite(RPM_FAN_PIN_GREEN, HIGH);
  attachInterrupt(RPM_FAN_PIN_GREEN, rpm_fan, FALLING);
  analogWrite(PWM_FAN_PIN_BLUE,speed); // Lüfter langsam starten
  
  letzte_ausgabe = millis();
}

void loop(){

  TempNTC = thermistor->readCelsius();
  if (TempNTC > RevTemp) {
    FanSpeed = MAX_SPEED; // PWM 255
  }
  if (TempNTC < RevTemp) {
    FanSpeed = MIN_SPEED; // PWM 50
  }

  if (FanSpeed > 255) {
    FanSpeed = 255;
  }

  analogWrite(PWM_FAN_PIN_BLUE, FanSpeed);
if ((millis() - letzte_ausgabe) >= 1000){

    // Interrupt deaktivieren um das rechnen nicht zu unterbrechen.
    detachInterrupt(RPM_FAN_PIN_GREEN);

    // RPM errechnen und ausgeben:
      FanSpeed = (counter_rpm * 60UL) / FAN_FLANKEN;
    Serial.print("FanSpeed : ");
    Serial.print(FanSpeed);
    Serial.println(" U pro Min");
 
    // Counter zuruecksetzen
    counter_rpm = 0;
 
    // Zeitpunkt setzen
    letzte_ausgabe = millis();

    attachInterrupt(RPM_FAN_PIN_GREEN, rpm_fan, FALLING);
}
}

void rpm_fan(){
  counter_rpm++;
}

Wiring:
Teensy4 and 4pin fan.png

I am not a programming professional.
I hope someone from you can help me?

greeting
Johnny
 
Many thanks for your response.

I corrected it in the sketch, but unfortunately it didn't bring any improvement.
Code:
/*
Lueftersteuerung RadiationShield
- PWM gesteuert bei uber- bzw unterschreiten eingestellter Schwellwerte
*/
#include <Thermistor.h>
#include <NTC_Thermistor.h>
#include <SmoothThermistor.h>
/********************************************/
/*                PIN-Section               */
/*      Hier Sensor-Pins definieren         */
/********************************************/
#define PWM_FAN_PIN_BLUE 4
#define RPM_FAN_PIN_GREEN 7
#define UPDATE_ZYKLUS 1000
#define MAX_SPEED 255
#define MIN_SPEED 50
#define FAN_FLANKEN 4

// Thermistor NTC10k
#define PIN_INPUT              14
#define REFERENCE_RESISTANCE   10000
#define NOMINAL_RESISTANCE     10000
#define NOMINAL_TEMPERATURE    30
#define B_VALUE                3950
/**
  Smoothing factor of a temperature value.
*/
#define SMOOTHING_FACTOR 1
Thermistor* thermistor = NULL;
float celsius;


/********************************************/
/*           Variablen-Section              */
/********************************************/
int speed;
int FanSpeedPWM = 0;
int FanSpeed = 0;
float TempNTC;
int RevTemp = 25;
unsigned long letzte_ausgabe = 0;
volatile int counter_rpm = 0;

/********************************************/
/*              Setup-Section               */
/********************************************/
void setup(){
  Serial.begin(9600);
  Thermistor* originThermistor = new NTC_Thermistor(
    PIN_INPUT,
    REFERENCE_RESISTANCE,
    NOMINAL_RESISTANCE,
    NOMINAL_TEMPERATURE,
    B_VALUE
  );
  thermistor = new SmoothThermistor(originThermistor, SMOOTHING_FACTOR);

  celsius = thermistor->readCelsius();
  
  pinMode(PWM_FAN_PIN_BLUE, OUTPUT);
  //digitalWriteFast(PWM_FAN_PIN_BLUE,LOW);
  speed=MIN_SPEED; //Langsam beginnen


  pinMode(RPM_FAN_PIN_GREEN, INPUT);
  digitalWrite(RPM_FAN_PIN_GREEN, HIGH);
  attachInterrupt(RPM_FAN_PIN_GREEN, rpm_fan, FALLING);
  analogWrite(PWM_FAN_PIN_BLUE,speed); // Lüfter langsam starten
  analogWriteFrequency(4,25000);
  letzte_ausgabe = millis();
}

void loop(){

  TempNTC = thermistor->readCelsius();
//  if (TempNTC > RevTemp) {
//    FanSpeedPWM = MAX_SPEED; // PWM 255
//  }
//  if (TempNTC < RevTemp) {
//    FanSpeedPWM = MIN_SPEED; // PWM 50
//  }

//  if (FanSpeedPWM > 255) {
    FanSpeedPWM = 255;
//  }
//    Serial.print("Temperatur : ");
//    Serial.println(TempNTC);
//    Serial.print("PWM : ");
//    Serial.println(FanSpeedPWM);

  analogWrite(PWM_FAN_PIN_BLUE, FanSpeedPWM);
if ((millis() - letzte_ausgabe) >= 1000){

    // Interrupt deaktivieren um das rechnen nicht zu unterbrechen.
    detachInterrupt(RPM_FAN_PIN_GREEN);

    // RPM errechnen und ausgeben:
    FanSpeed = (counter_rpm * 60UL) / FAN_FLANKEN;
    Serial.print("FanSpeed : ");
    Serial.println(FanSpeed);
    Serial.println(" U pro Min");
 
    // Counter zuruecksetzen
    counter_rpm = 0;
 
    // Zeitpunkt setzen
    letzte_ausgabe = millis();

    attachInterrupt(RPM_FAN_PIN_GREEN, rpm_fan, FALLING);
}
}

void rpm_fan(){
  counter_rpm++;
}

The Noctua nf-a8 fan has a maximum speed of 2200 rpm and a pwm frequency of 25kHz. With my sketch I get the following speeds:
Code:
FanSpeed : 472335
 U pro Min
FanSpeed : 472455
 U pro Min
FanSpeed : 472065
 U pro Min

damn :confused:

I've have found a specifiations paper from noctua.
If I use the formula according to the document, the speed is still not correct.
The interrupts per second are too high in my routine.

Does anyone have any idea how I could solve this?

greeting
Johnny
 

Attachments

  • Noctua_PWM_specifications_white_paper.pdf
    1.1 MB · Views: 1,457
Hi Johnny,

1. You try to activate the input pullup on the RPM_FAN_PIN_GREEN by writing HIGH to the INPUT pin. This is seemingly (looking at cores/teensy4/digital.c:digitalWrite) not implemented on the Teensy 4 yet. It's also something of a legacy; the recommended way is rather to set pinMode to INPUT_PULLUP. Can you please try that?

2. Your FAN_FLANKEN is 4, but according to the PDF it should be 2.

3. Teensy 4 is not 5V-resilient. The PDF states for the PWM control input signal: "External pull-up is not necessary as the signal is pulled up to 3,3V/5V inside the fan." But it also states: "There is very little current sourced from the fan". So the 3,3V/5V (whatever that is) external pull-up might not harm your Teensy 4 ...

HTH

Kind regards,
Sebastian
 
Hello Sebastian,

Changed your suggestions in the sketch but brought no improvement.
The change with the external pullup did not improve either.
The sketch with the fan runs smoothly on the Arduino, only not on the Teensy.
With the Arduino I have the following timer function in the setup routine:
Code:
TCCR1B = TCCR1B & 0b11111000 | 0x01;
But this is not possible with the Teensy.

Kind regards,
Johnny
 
With the Arduino I have the following timer function in the setup routine:
Code:
TCCR1B = TCCR1B & 0b11111000 | 0x01;

That is the low-level Arduino way of increasing the PWM frequency on pins D9 and D10 that are controlled by Timer 1 on Atmega328. Your analogWriteFrequency(4,25000) replaces that.

Changed your suggestions in the sketch but brought no improvement.

Maybe you should simplify your sketch first. Is the fan reacting to any PWM signal on D4 at all? According to the FanSpeed values given above you are receiving about 31500 FALLING interrupts every second -- do you have a logic analyser or oscilloscope that you could connect to D7 to see what is really going on?

Kind regards,
Sebastian
 
You could also try using the FreqMeasure library. It has an example in File > Examples > FreqMeasure which just reads the frequency and prints to the serial monitor. Maybe try that and see if the measured frequency is correct?
 
I have now checked on pin 7 with the oscilloscope and the pulse widths are correct (depending on the speed).
If I measure the frequency with FreqMeasure then it is also not correct.
I have to check that with a second teensy 4.0.
Maybe there's something about the hardware.
I'll get back.

Johnny
 
I know, it's an old thread, but the solution is missing. PrinzEisenherz expected that his calculation takes place after exactly 1000ms. But >=1000 means, it could have been started later. And this time need to be considered:

Code:
int samplinginterval = millis() - letzte_ausgabe;
FanSpeed = (counter_rpm * 60UL) / FAN_FLANKEN * 1000 / samplinginterval;

It's working wirth my Noctua fan and Teensy 4.1. But have in mind, that a sampling interval of 1s is quite short for low-rpm fans. At 300rpm you get only 10 pulses per second, which result in a bad resolution.
 
Trying to revive this old thread, was there ever a resolution? I'm getting the same issue as PrinzEisenherz. Noctua RPM fan speed working on an UNO and a Teensy 3.2. Tried it on and 4.0 (and on an ATSAMD51) and got very high RPM readings.

When I unhook the PWM pin it seems to read the RPM correctly. It looks like the PWM frequency is leaking onto the RPM pin. Any ideas on how to filter the "noise" out?
 
Hi DreamWeaver,
Last year i had found a solution about the problem with very high RPM readings.
I've no time today, but i will check tomorrow and send you the correct wiring and code fromm my sketch.

Johnny
 
Hi DreamWeaver,
here is my wiring of the Noctua fan and the teensy 4.
Teensy-fan-control.png
and here my testsketch
Code:
/*
Fan-testsketch
*/

/********************************************/
/*                PIN-Section               */
/********************************************/
#define PIN_FAN_PWM_BLUE 4
#define PIN_FAN_TACH_GREEN 7
#define UPDATE_ZYKLUS 1000
#define MAX_SPEED 255
#define MIN_SPEED 50
#define FAN_FLANKEN 2.0



/********************************************/
/*           Variablen-Section              */
/********************************************/
unsigned long last_update = 0;
volatile int counter_rpm = 0;


void rpm_fan(){
  counter_rpm++;
}


/********************************************/
/*              Setup-Section               */
/********************************************/
void setup(){
  Serial.begin(9600);
  pinMode(PIN_FAN_TACH_GREEN, INPUT);
  digitalWrite(PIN_FAN_TACH_GREEN, HIGH);
  attachInterrupt(PIN_FAN_TACH_GREEN, rpm_fan, FALLING);
 
  pinMode(PIN_FAN_PWM_BLUE, OUTPUT);
  analogWriteFrequency(PIN_FAN_PWM_BLUE, 25000);
  analogWrite(PIN_FAN_PWM_BLUE, MIN_SPEED); // Lüfter min speed
  delay(5000);
  analogWrite(PIN_FAN_PWM_BLUE, MAX_SPEED); // Lüfter max speed
//  analogWriteFrequency(PIN_FAN_PWM_BLUE, 25000);
  last_update = millis();
}

void loop(){
if ((millis() - last_update) >= 1000){
    unsigned long samplinginterval = millis() - last_update;
    float FanSpeedCalc;
    uint16_t FanSpeed;
    detachInterrupt(digitalPinToInterrupt(PIN_FAN_TACH_GREEN));

    FanSpeedCalc = (float)counter_rpm * 60000.0 / (float)samplinginterval / FAN_FLANKEN;
    FanSpeed = (uint16_t)FanSpeedCalc;
    Serial.print("count : ");
    Serial.println(counter_rpm);

    Serial.print("FanSpeed : ");
    Serial.print(FanSpeed);
    Serial.println(" U pro Min");

    counter_rpm = 0;
    last_update = millis();

    attachInterrupt(PIN_FAN_TACH_GREEN, rpm_fan, FALLING);
  }
}

I hope it will help you further.
For me it works without problems.
greeting
johnny
 
Back
Top