Fan Speed AND DHT22 (temp/humidity)

I'm having a problem determining the Fan RPM when I run both Fan and DHT22 sensor. It's a logic problem, old age, etc. :wink: I've attached my Sketch ... I think it's to do with the frequency of the loop vs. the attachInterrupt. My calculations and/or timings are off as I'm getting 5X or more higher RPM results.

Everything else is working great, fan speed increases decreases based on temps.

Sketch_4PIN_FanController.ino (4.1 KB)

Since Arduino doesn't support multiple loops, I tried to work two code paths that don't block.

Thoughts?

Cheers, Rob.

Hi, @robainscough

Please read the post at the start of any forum , entitled "How to use this Forum".

Can you also post your circuit diagram please, just a hand drawn schematic would be fine.

Thanks... Tom.. :smiley: :+1: :coffee: :australia:

Hi Tom,

I'm not sure why you feel my post needs that link to "How to get the best out of this forum"? Did I violate some terms of use ... do something wrong?

Schematic isn't necessary, this is a simple "logic" issue with my code/sketch. The DHT22 should only be polled every 2-3 seconds which is working great. The Fan RPM is triggered by RISING edge (two per cycle) ... I'm counting the pulses over 1 second then reset the count and do some basic math to determine RPM.

I can get my RPM to be accurate when I execute the code in void Loop() but that "blocks" the execution since it runs a tight loop for 1 second to determine number of pulses. I'm trying to execute two "threaded" (in normal programming world) processes and Arduino sketches don't have any mechanism for threading so I'm managing execute via time checks. Every 3000 ms I check the DHT22 and every 1000ms I check the Fan pulse count.

The circuit is doing everything I want it to do, but I'm just not calculating the RPM correctly - logic issue not a schematic one.

Cheers, Rob.

That link contains the answers to your question.

No it doesn't.

Yikes, I regret coming here ... sorry to have bothered.

It's been maintained and updated over many years, so if there is something there that is not clear, please explain what is missing, so it can be improved.

Or alternatively you can explain why you think that link "answers" my question? I honestly don't get this stick beating lesson that only you seem to know what the "lesson" is about?

If I've done something that violates terms, please just say it and stop the indirection ... is that clear?

Mainly, post your sketch inline not as a link. You're not being attacked, just being asked to follow time honoured methods that result in faster resolution and a much easier experience for the volunteer helpers here.

The whole point of the link, is to save the endless reposting of the same kind of requests for information and data in usable formats.

Per quote from the link provided:

"Be aware that there is a limit of 120000 characters for a post. If your code takes you above this limit then you can add your .ino file as an attachment. "

So I provided the .ino file. Anyway, we're several posts into this with NOTHING that is specific to "my" issue that could be useful for others (or not).

I get that you/others don't want to repeat requests, I did do a search first and didn't find anything specific to my issue, hence my post. I'll admit, I don't know if it's 120000 characters or more, but I felt it was large enough to not try and paste as code in a window that would require scrolling and for many be visually unmanageable (especially those on smaller screens like iPads, iPhones, etc.).

Anyway, if you feel you want to beat me with a stick because my .ino may not be 120000 characters (typically I don't count the number of characters in my Sketches) then perhaps just avoid this thread?

'count' is an int, risking overflow, especially when you multiply it by 60/2. It's also not declared as volatile, and it's not read atomically. There may be other ISR no-no's that I missed.

Hi,
@robainscough
Your code;

// Example sketch for DHT22 humidity - temperature sensor
// Written by cactus.io, with thanks to Adafruit for bits of their library. public domain

#include "cactus_io_DHT22.h"

#define DHT22_PIN 4 // what pin on the arduino is the DHT22 data line connected to
 
// For details on how to hookup the DHT22 sensor to the Arduino then checkout this page
// http://cactus.io/hookups/sensors/temperature-humidity/dht22/hookup-arduino-to-dht22-temp-humidity-sensor
 
// Initialize DHT sensor for normal 16mhz Arduino.
DHT22 dht(DHT22_PIN);
// Note: If you are using a board with a faster processor than 16MHz then you need
// to declare an instance of the DHT22 using
// DHT22 dht(DHT22_DATA_PIN, 30);
// The additional parameter, in this case here is 30 is used to increase the number of
// cycles transitioning between bits on the data and clock lines. For the
// Arduino boards that run at 84MHz the value of 30 should be about right.

float temperature;
float dewpoint;
const int fan_control_pin = 3;
int count = 0;
unsigned long start_time;
int rpm;

void setup() {
  // put your setup code here, to run once:
  pinMode(fan_control_pin, OUTPUT);
  analogWrite(fan_control_pin, 0);
  Serial.begin(9600);
  dht.begin();
  attachInterrupt(digitalPinToInterrupt(2), counter, RISING);
}

void loop() {

  // TODO: Need to have two frequency checks, 
  //       Temp/Humidity every 3000 milliseconds
  //       Fan RPM every 1000 milliseconds
  //       Good sample on how to have multiple operations running in single Loop:
  //           https://arduino.stackexchange.com/questions/37684/can-i-make-multiple-void-loops-with-arduino-uno/37685 
  
  readTempAndHumidity();

// TODO: Need to rethink, since readFanSpeed() 
// Calculate duration using now/time (millis)
  readFanSpeed();

}

void readTempAndHumidity(){

  static unsigned long lastTime = 0;
  const long interval = 3000;
  static bool state = 0;

  unsigned long now = millis();

  if ( now - lastTime > interval && state == 0) {
    state = 1;
    lastTime = now;
    // TODO: Code check values and increase fan speed

  // put your main code here, to run repeatedly:
  dht.readHumidity();
  dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(dht.humidity) || isnan(dht.temperature_C)) {
  Serial.println("DHT sensor read failure!");
  return;
  }

  temperature = dht.temperature_F;
  
  Serial.print(dht.humidity); Serial.print(" %\t\t");
  Serial.print(dht.temperature_C); Serial.print(" *C\t");
  Serial.print(dht.temperature_F); Serial.print(" *F\t");
  Serial.print(dht.computeHeatIndex_C()); Serial.print(" *C\t");
  Serial.print(dht.computeHeatIndex_F()); Serial.println(" *F");
  
  // Wait a few seconds between measurements. The DHT22 should not be read at a higher frequency of
  // about once every 2 seconds. So we add a 3 second delay to cover this.
  // delay(3000);
  
  // temperature = float(analogRead(A0)) * 500.0 / 1024.0;
  // temperature = float(analogRead(A0)) * 500.0 / 1024.0;
  if (temperature < 60.0) analogWrite(fan_control_pin, 0);
  if ((temperature >= 60.0) && (temperature < 80.0)) analogWrite(fan_control_pin, 126);
  else analogWrite(fan_control_pin, 255);  
    
  }

  if ( now - lastTime > interval && state == 1) {
    state = 0;
    lastTime = now;
    // Reset interval
  }
  
}

void readFanSpeed(){

  static unsigned long flastTime = 0;
  const long finterval = 1000;
  static bool fstate = 0;

  unsigned long now = millis();

  if ( now - flastTime > finterval && fstate == 0) {
    fstate = 1;
    flastTime = now;

    rpm = count * 60 / 2;

    // Debug
    Serial.print(count);
    Serial.println(" count");
    Serial.print(flastTime);
    Serial.println(" lasttime");

    count = 0;

    Serial.print("The fan speed is ");
    Serial.print(rpm);
    Serial.println(" rpm");
    
  }

  if ( now - flastTime > finterval && fstate == 1) {
    fstate = 0;
    flastTime = now;
    // Reset interval
    count = 0;
  }
    
    // TODO: read Fan Speed

}

void counter() {
  count++;
}

This is what your code looks like in code tags.
It is easier to read for those not using a PC or Laptop platform.

Sorry for the misunderstanding, but I thought you did not know how to use code tags.

Tom.... :smiley: :+1: :coffee: :australia:

Unfortunately using volatile didn't resolve my issue ... I can get the my rpm logic to work correctly with accurate RPM if I don't call/reference readTempAndHumidity() and just call/reference readFanSpeed().

Maybe there is something going on that's conflicting in the DHT22 library?

could be

Hi,

Have you got code that just measures the RPM with no reference to the DHT22?

You might have to try other DHT22 libraries.
I am not familiar with cactus_io_DHT22.h library.

Tom... :smiley: :+1: :coffee: :australia:

Every time you change your code, post it here in its entirety (inline using code tags as @ TomGeorge was forced to show you in Reply #11). That way people can see what you've already done and make further suggestions.

Also, did you address the other items @aarg mentioned in Reply #10? Possible overflow? Atomically reading 'count' in non-ISR code?

Also, you haven't bothered to tell us which "Arduino" board you're using. It matters.

Also, why is 'count' an int? It's never going to be negative. So, you're wasting half of the values presentable by the size of the variable. Use an unsigned integer type.

Finally, look at the source code of the DH22 library you're using. Does it ever disable interrupts? Those devices have specific timing requirements and interrupts may be disabled to meet them. If two or more pulses occur on your interrupt pin while interrupts are disabled, you will miss counts.

Thanks for the responses. So I went back to the basics just testing basic Fan RPM that "I thought" was working correctly ... it's actually not working correctly (I just got the output close to what I expected at the setting I expected 255/max).

Here is the modified code to simply test Fan RPM exclusively (at 255, 126, and 0):

// UNO R3
// Set a fan speed 0-255  (Corsair SP120 4 wire PWM) 12V DC 0.25A
// 4 Black wires with pin 1 Identified ... see reference: https://landing.coolermaster.com/faq/3-pin-and-4-pin-fan-wire-diagrams/

const int fan_control_pin = 3;
unsigned long start_time;
volatile unsigned long count = 0;
int rpm;

void setup() {

  // put your setup code here, to run once:
  pinMode(fan_control_pin, OUTPUT);
  analogWrite(fan_control_pin, 0);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(2), counter, RISING);

}

void loop() {

  // Test, max speed for this fan is 2350 RPM  
  //       Actual reported fan speed is 2160 @ 255 with 0.11A draw
  //       Actual reported fan speed is 8400 @ 126 with 0.03A draw  ???
  //       Actual reported fan speed is 750 @ 0 with 0.02A draw
  analogWrite(fan_control_pin, 255);   

  // Fan RPM
  start_time = millis();
  count = 0;
  while((millis() - start_time) < 1000) 
  {
    // 1 second pause so count increments
  }

  rpm = count * 60 / 2;

  Serial.print("The fan speed is ");
  Serial.print(rpm);
  Serial.println(" rpm");

}

void counter() 
{
  count++;
}

Here is my original project Schematic (with Temp/Humidity control) ... sorry Fusion 360 was taking too long for me, so I just quickly made this with Visio with actual parts and output to a PNG (I know you folks hate image files):

The Parts:
Arduino UNO R3
Corsair Fan SP120 2350 RPM 12V DC 0.25A
Mean Well RSP 320-24 PSU
AITRIP DHT22 AM2302
LM2596 DC-DC Buck Converter
10K Resistor 1/4 watt 1%

Perhaps my circuit is wrong or my logic is wrong. I found this thread here that use a different approach with attachInterrupt and detachInterrupt using PWM library ... see thread: Reading Fan RPM

Thoughts?

Cheers, Rob.

Hi,
Can you check that ALL gnds are connected together?
Especially through the DC-DC converters.

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

You lost me there, you mean have a seperate ground junction box for all components?

I've measured the output at various points and they all seem correct. Yellow wire to the Fan reports 1.5V DC. Blue wire is 5.1V DC.

After reading the link to "Reading Fan RPM", I'm puzzled that the OP indicated fan RPM is 11,900 at 100% duty, that seems extremely high for a PC fan? Wonder what load the OP ran at?

I have an external fan controller designed for PCs that reports RPM of my Corsair and it's pretty close to 2300.

If you take a look at my Sketch comments I have 2160 RPM reported at 255 (max), 8400 RPM at 126 which is very odd, and 750 RPM at 0 even more odd. Visually the Fan is clearly running faster at 255 vs 126 .. so something must be wrong in my method of calculation?

Cheers, Rob.

Adjusted the code as follows but still not getting correct RPM:

// UNO R3
// Set a fan speed 0-255  (Corsair SP120 4 wire PWM) 12V DC 0.25A
// 4 Black wires with pin 1 Identified ... see reference: https://landing.coolermaster.com/faq/3-pin-and-4-pin-fan-wire-diagrams/

const int fan_control_pin = 3;
unsigned long start_time;
volatile unsigned int count = 0;
int rpm;

void setup() {

  // put your setup code here, to run once:
  pinMode(fan_control_pin, OUTPUT);
  analogWrite(fan_control_pin, 0);
  Serial.begin(9600);
  // attachInterrupt(digitalPinToInterrupt(2), counter, RISING);

}

void loop() {

  // Test, max speed for this fan is 2350 RPM  
  //       Actual reported fan speed is 2160 @ 255 with 0.11A draw
  //       Actual reported fan speed is 8400 @ 126 with 0.03A draw  ???
  //       Actual reported fan speed is 750 @ 0 with 0.02A draw
 
  analogWrite(fan_control_pin, 255);   

  // Fan RPM
  start_time = millis();
  count = 0;
  attachInterrupt(digitalPinToInterrupt(2), counter, FALLING);
  while((millis() - start_time) < 1000) 
  {
    // 1 second pause so count increments
  }
  detachInterrupt(digitalPinToInterrupt(2));

  // rpm = count * 60 / 2;
  rpm = (count/2)*6;

  Serial.print("The fan speed is ");
  Serial.print(rpm);
  Serial.print(" rpm - count: ");
  Serial.println(count);

}

void counter() 
{
  count++;
}

When I increase the value: analogWrite(fan_control_pin, 255) my "count" value decreases to 71, yet I can clearly see and hear the fan speed has increased. If I analogWrite(fan_control_pin, 126) my "count" value increases 230-247 range, yet I can clearly see and hear the fan speed reduce.

IRS is set correctly per my Sketch (no parameters, no returns). My digital pin for Interrupts is pin 2 which is correct for the UNO (2 or 3).

I'm 99% certain I have the Corsair fan output wiring correct as per image:

I'm stuck, perhaps I have a bad Uno R3? It's an act "actual" Arduino and not a knock-off.

EDIT: I tried RISING and FALLING.

Rob.

Try setting the pinMode of pin2 to INPUT_PULLUP in setup, or perhaps right before the attachinterrupt() call.

The input pin maybe floating.