Temperature controlled fan with One wire DS18B20 sensor with threshold

Thought I'd share my code, I've messed with this over the past couple of days. I wanted to have a threshold which would prevent the program from turning on the fan immediately.

Basic program:
Check temp;
-----> if higher than setpoint + threshold, then turn fan on
-----> if between setpoint and setpoint + threshold, check previous fan state,
----------> if was on, stay on
----------> if was off, stay off
-----> if lower than setpoint, then turn fan off

Right now its setup to print a lot of info, but you can remove it as you see fit.

#include <OneWire.h>


OneWire  ds(10);                    // on pin 10

int PinLED = 13;                     //LED Pin
float temp_setpoint = 75.00;    //Temperature Set Point
float temp_thresh = 2.50;        //Temperature threshold
  
void setup(void) {
  Serial.begin(9600);
  pinMode(PinLED,OUTPUT);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
  if ( !ds.search(addr)) {
    ds.reset_search();
    delay(250);
    return;
  }
  
  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);                          // start conversion, with parasite power on at the end
  
  delay(750);                               // maybe 750ms is enough, maybe not
                                             // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);                            // Read Scratchpad

  for ( i = 0; i < 9; i++) {                 // we need 9 bytes
    data[i] = ds.read();
  }

                                             // convert the data to actual temperature
  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;                          // 9 bit resolution default
    if (data[7] == 0x10) {
                                             // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;         // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw << 2;    // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw << 1;    // 11 bit res, 375 ms
  }

  fahrenheit = (float)raw / 16.0 * 1.8 + 32.0;
  float temp_current = fahrenheit;
  Serial.print(", Fan Relay: ");

  if (temp_current >= temp_setpoint + temp_thresh) {
   Serial.print("ON ");
   digitalWrite(PinLED, 1);
  }
else if (temp_current >= temp_setpoint && temp_current <= temp_setpoint + temp_thresh) {
    int fan1state = digitalRead(PinLED);
       if (fan1state == 1){
           Serial.print("ON ");
           digitalWrite(PinLED, 1);
       }
       else {
           Serial.print("OFF");
           digitalWrite(PinLED, 0);
       }  
  }
  else if (temp_current <= temp_setpoint) {
   Serial.print("OFF");
   digitalWrite(PinLED, LOW);
  }
  
  Serial.print(",  Current Temp / Threshold: ");
  Serial.print(fahrenheit);
  Serial.print(" / ");  
  Serial.print(temp_setpoint + temp_thresh);  
  Serial.print(" (");
  Serial.print(temp_current >= temp_setpoint + temp_thresh);  
  Serial.println(")");  

}

So I have messed around with this and have found out that if I put a load on pin 13, reading its state no longer returns the expected result. I want it to tell me that it was previously on but it defaults to 0 state. Connecting an NPN transistor to drive a relay as load.

Any advice appreciated.

Split your program up in several functions that do one task to simplify the loop() to something like this

void loop()
{
  float t = readTemperature();

  if (t > setpoint+threshold)
  {
    state = ON;
  }
  if (t < setpoint)
  {
    state = OFF;
  }

  setFan(state);
}

the part below makes no difference in state...

-----> if between setpoint and setpoint + threshold, check previous fan state,
----------> if was on, stay on
----------> if was off, stay off

robtillaart:
Split your program up in several functions that do one task to simplify the loop() to something like this

void loop()

{
  float t = readTemperature();

if (t > setpoint+threshold)
  {
    state = ON;
  }
  if (t < setpoint)
  {
    state = OFF;
  }

setFan(state);
}




the part below makes no difference in state...


> -----> if between setpoint and setpoint + threshold, check previous fan state,
> ----------> if was on, stay on
> ----------> if was off, stay off

Thanks for your help.

But I have to disagree with you, that part does make a difference. Or at least I want it to make a difference, I may just not be programming it correctly. The threshold is designed to prevent excessive cycling of the fan. I want the fan to only turn on when the temp > setpoint + threshold AND it needs to stay on until temp < setpoint.

I could rewrite the program to be an if then based on output pin state, but based on the trouble with the pin beg read low when it should be high that I've been seeing, I'll hold off on this.

I want the fan to only turn on when the temp > setpoint + threshold AND it needs to stay on until temp < setpoint.

That's exactly what Robtillart's code does.

Here is a more complete code, take a pen and paper and iterate over loop and let the temperature go from 15 - 30 and back to 15 and write dowwn state for every temp. This will help to understand how the code works. I learned a lot of programming with paper and pencil and being "the interpreter" myself :slight_smile:

#define FANPIN 5

int state = LOW;     // note this is a global variable IMPORTANT
int setpoint = 20;
int threshold = 5;

void setup()
{
  Serial.begin(115200);
  Serial.println("Start...");
  pinMode(FANPIN, OUTPUT);
}
  

void loop()
{
  float t = readTemperature();

  if (t > setpoint+threshold)
  {
    state = ON;
  }
  if (t < setpoint)
  {
    state = OFF;
  }

  Serial.print("temp: ");
  Serial.print(t, 2);              // 2 decimals
  Serial.print("\t\tstate: ");
  Serial.print(state);

  setFan(state);
}

void setFan(int state)
{
  digitalWrite(FANPIN, state);
}

float readTemperature()
{
  float t = analogRead(A0);   // assuming an analog LM35 sensor , other code
  t = t * 5000 / 1023;           // make millivolts of it
  t = -25 + t/10;                  // 1C per 10 mv  (dont know exact formula)
}

Succes!

poldim:
So I have messed around with this and have found out that if I put a load on pin 13, reading its state no longer returns the expected result. I want it to tell me that it was previously on but it defaults to 0 state. Connecting an NPN transistor to drive a relay as load.

Any advice appreciated.

Erm, you connected the NPN how exactly? If the pin is set to HIGH and you are reading LOW that means you're grossly overloading it (which can fry the chip) - or that its configured as an INPUT...

I'm going to to the same thing with my code which uses DS18B20.h to show the temperature and control the heat-sink fan.

Since the temperature rises very slowly, it reaches many times to the "set point" and comes down and

this makes the microcontroller to turn on and off the fan when temperature gets close to "setpoint" and plays around 49-50.
In result fan starts and stops tens of times until temperature stays on 50 or goes above; then it stays on.

Setpoint is set to 50 degrees in this line in DS18B20.h file.

if(((temp_whole/10)%10) >= 5) PORTD.F1=1; // Aici setez temp de pornire cooler [5=50 grade] (maximum setpoint temperature to turn on fan 5=50 degrees)

So i'm asking the same question like "Poldim".
I want the fan to only turn on when the temp > setpoint + threshold AND it needs to stay on until temp < setpoint.

Do you guys can help me change this code?

VAM code.zip (3.61 KB)

Had a quick look at your code, you really need to restructure it.

The problem comes down to what is called hysteresis. This is the way to handle it:

void loop()
{
  float temperature = readTemperature();
  if (temperature > 50)  fanOn();
  if (temperature < 48)  fanOff();
  displayTemperature(temperature);
}

Note that there is a different temperature to switch on and a different to switch off the fan.