Fan and display "pulses" when at threshold temp and I don't want it to!

Project....a power supply
Hardware...well regulated 14.4v DC to 12v regulator that feeds knock-off NANO and fan circuit, that 14.4v supply also goes to XL6019 to raise the volts to 30v to a LM317T circuit with a POT.
An INA260 monitors that output. I have 3 DS18B20's monitoring 3 points. all is displayed on an OLED.
Problem...when the 18B20's (1 or more) reach threshold and turn on the fan circuit, the display and fan pulses to the speed of the loop or delay if I have that set.
I have documented the attached sketch well.
I think I may have made a mistake in the order of execution or I need to somehow isolate the "if" statement. 
I would like some help understanding my mistake and fixing it with the members help.
Thanks, Ben.

```cpp
/////////////////////Library's for wire communication///////////////////////////
#include <Wire.h>     //I2C libray
#include <OneWire.h>  //One Wire library
////////////////////////////////////////////////////////////////////////////////

/////////////////////Library for Dallas DS18B20 temp sensor//////////////////////
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);        // Create instance of oneWire class
DallasTemperature sensors(&oneWire);  // Pass the oneWire reference to DallasTemperature library

uint8_t LM317T[8] = { 0x28, 0x2C, 0x23, 0x58, 0x00, 0x00, 0x00, 0xA7 };
uint8_t XL6019[8] = { 0x28, 0xB4, 0x66, 0x59, 0x00, 0x00, 0x00, 0x03 };
uint8_t MOSFET[8] = { 0x28, 0xBB, 0x3D, 0x56, 0x00, 0x00, 0x00, 0x58 };  // maybe not use a MOSFET to break the circuit on "everythingOff" ISR or temp threshold.
////////////////////////////////////////////////////////////////////////////////

//////////////////////Library's for OLED 128 x 64 display///////////////////////
#include <Adafruit_GFX.h>      // Graphics library
#include <Adafruit_SSD1306.h>  // Text library

#define SCREEN_WIDTH 128     // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels
#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
////////////////////////////////////////////////////////////////////////////////

///////////////////////Library for INA260///////////////////////////////////////
#include <Adafruit_INA260.h>

Adafruit_INA260 ina260 = Adafruit_INA260();
////////////////////////////////////////////////////////////////////////////////

//////////////////////////INPUT/OUTPUT PINS/////////////////////////////////////
#define OFF_BTN 3  // Give it an interupt pin
#define BUZZER 4   // not used yet
#define FAN_PIN 5
#define PANIC 6  // still figuring it out
////////////////////////////////////////////////////////////////////////////////

void setup() {
  pinMode(OFF_BTN, INPUT);  // not used yet
  pinMode(BUZZER, OUTPUT);  // not used yet
  pinMode(FAN_PIN, OUTPUT);
  pinMode(PANIC, OUTPUT);  // not used yet. Will probably need a pull-up resistor if it stays high.?

  attachInterrupt(digitalPinToInterrupt(OFF_BTN), everythingOff, RISING);  // not used yet

  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  Wire.begin();
  sensors.begin();
  ina260.begin();
}

void loop() {
  sensors.setResolution(12);
  sensors.requestTemperatures();

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);

  display.setCursor(0, 0);
  display.print("OUTPUT:");
  display.setCursor(54, 0);
  display.print("SENSOR/TEMP:");

  display.setCursor(0, 20);
  display.print(ina260.readBusVoltage() / 1000, 3);
  display.print("V");
  display.setCursor(48, 20);
  display.print("  REG ");
  printTemperature(LM317T);

  display.setCursor(0, 30);
  display.print(ina260.readCurrent() / 1000, 3);
  display.print("A");
  display.setCursor(48, 30);
  display.print("  CNV ");
  printTemperature(XL6019);

  display.setCursor(0, 40);
  display.print(ina260.readPower() / 1000);
  display.print("W");
  display.setCursor(48, 40);
  display.print("  FET ");
  printTemperature(MOSFET);

  display.display();
}

void everythingOff() {  // not used yet
  digitalWrite(PANIC, LOW);
}

void printTemperature(DeviceAddress deviceAddress) {
  float tempC = sensors.getTempC(deviceAddress);
  display.print(tempC);
  display.print((char)247);
  display.print("C");

  if (tempC >= 18) {              //////////////////////Start of the issue?/////////////////////////////////////
    digitalWrite(FAN_PIN, HIGH);  // it seems to my "new at this" thinking, I missed a fundamental concept to isolate this "if" statement
    display.setCursor(0, 50);     // or something so the fan will just be actuated without effecting or be affected by the rest of the code loop
    display.print("COOLING FAN ON!");
    display.display();  // the pulsing is also affected by how many DS18B20's are above threshold as how many lines on display are blinking
  }                     // PS: obviously the threshold is set low for testing
  if (tempC < 18) {
    digitalWrite(FAN_PIN, LOW);  ///////////////////////End of the issue?///////////////////////////////////////
    delay(500);                  // fan and display pulses with the execution speed of the loop, or with delay(500), whichever is longer
  }
}


Google hysteresis.

Then put it in your code

  if (tempC > 21) {              //////////////////////Start of the issue?/////////////////////////////////////
    digitalWrite(FAN_PIN, HIGH);  // it seems to my "new at this" thinking, I missed a fundamental concept to isolate this "if" statement
    display.setCursor(0, 50);     // or something so the fan will just be actuated without effecting or be affected by the rest of the code loop
    display.print("COOLING FAN ON!");
    display.display();  // the pulsing is also affected by how many DS18B20's are above threshold as how many lines on display are blinking
  }                     // PS: obviously the threshold is set low for testing
  if (tempC < 17) {
    digitalWrite(FAN_PIN, LOW);  ///////////////////////End of the issue?///////////////////////////////////////
    delay(500);                  // fan and display pulses with the execution speed of the loop, or with delay(500), whichever is longer
  }

It's just like your home heating or cooling, you need a deadband where it won't turn off something that is running and won't turn in something that isn't.

Cleared of all that junk

  if (tooHot) turnOffTheHeat();

  if (tooCold) turnOnTheHeat();

where too hot might mean > 17 degrees and too cold might mean less than 15.

a7

Thanks, a7. Good idea. I used to work in HVAC. Forgot about chattering relays and thermostats.
I built some hysteresis in as suggested.
Still got a problem with this description below;
3 sensors, values on 3 lines on the display;
When one reaches threshold, the fan pulses and the lower 2 on display flashes at the same rate.
The next one triggered, just the 3rd line flashes while the fan pulses. If all sensors are over threshold , everything works well. I’m new, but it’s got to be something about order of execution it seems. It seems the code should hand off the fan execution and just go on monitoring “tempC”. But, like I said, I’m new and missing fundamentals.
Ben

The printTemperature function is called 4 x in loop,
Its name states it prints the Temp, but it also switches the fan and it has a delay in it
so that should be split

Create a separate global variable for the state of the fan

bool runFan = false;

At the begin of the loop you set runFan to false;

The print temperature can change the state of the var runFan to true.

void printTemperature(DeviceAddress deviceAddress) {
  float tempC = sensors.getTempC(deviceAddress);
  display.print(tempC);
  display.print((char)247);
  display.print("C");

  if (tempC >= 18)  runFan = true;
}

....

void controlFan()
{
  digitalWrite(FAN_PIN, runFan);
  if (runFan) display(fan on something);
  else display (fan off something)
}

At the end of loop you add two calls

controlFan();
delay(500);

Get the idea?

Thanks Rob.
Yes, I have the concept, now I’ve got to wrap my head around it. I implemented your solution to my best understanding and I’m getting errors that I want to research and fix. I will reply very soon.
Ben


```cpp
/////////////////////Library's for wire communication///////////////////////////
#include <Wire.h>     //I2C libray
#include <OneWire.h>  //One Wire library
////////////////////////////////////////////////////////////////////////////////

/////////////////////Library for Dallas DS18B20 temp sensor//////////////////////
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);        // Create instance of oneWire class
DallasTemperature sensors(&oneWire);  // Pass the oneWire reference to DallasTemperature library

uint8_t LM317T[8] = { 0x28, 0x2C, 0x23, 0x58, 0x00, 0x00, 0x00, 0xA7 };
uint8_t XL6019[8] = { 0x28, 0xB4, 0x66, 0x59, 0x00, 0x00, 0x00, 0x03 };
uint8_t MOSFET[8] = { 0x28, 0xBB, 0x3D, 0x56, 0x00, 0x00, 0x00, 0x58 };  // maybe not use a MOSFET to break the circuit on "everythingOff" ISR or temp threshold.
////////////////////////////////////////////////////////////////////////////////

//////////////////////Library's for OLED 128 x 64 display///////////////////////
#include <Adafruit_GFX.h>      // Graphics library
#include <Adafruit_SSD1306.h>  // Text library

#define SCREEN_WIDTH 128     // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels
#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  // See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
////////////////////////////////////////////////////////////////////////////////

///////////////////////Library for INA260///////////////////////////////////////
#include <Adafruit_INA260.h>

Adafruit_INA260 ina260 = Adafruit_INA260();
////////////////////////////////////////////////////////////////////////////////

//////////////////////////INPUT/OUTPUT PINS/////////////////////////////////////
#define OFF_BTN 3  // Give it an interupt pin
#define BUZZER 4   // not used yet
#define FAN_PIN 5
#define PANIC 6  // still figuring it out
////////////////////////////////////////////////////////////////////////////////

bool runFan = false;

void setup() {
  pinMode(OFF_BTN, INPUT);  // not used yet
  pinMode(BUZZER, OUTPUT);  // not used yet
  pinMode(FAN_PIN, OUTPUT);
  pinMode(PANIC, OUTPUT);  // not used yet. Will probably need a pull-up resistor if it stays high.?

  attachInterrupt(digitalPinToInterrupt(OFF_BTN), everythingOff, RISING);  // not used yet

  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  Wire.begin();
  sensors.begin();
  ina260.begin();
}

void loop() {
  sensors.setResolution(12);
  sensors.requestTemperatures();

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);

  display.setCursor(0, 0);
  display.print("OUTPUT:");
  display.setCursor(54, 0);
  display.print("SENSOR/TEMP:");

  display.setCursor(0, 20);
  display.print(ina260.readBusVoltage() / 1000, 3);
  display.print("V");
  display.setCursor(48, 20);
  display.print(" REG ");
  printTemperature(LM317T);

  display.setCursor(0, 30);
  display.print(ina260.readCurrent() / 1000, 3);
  display.print("A");
  display.setCursor(48, 30);
  display.print(" CNV ");
  printTemperature(XL6019);

  display.setCursor(0, 40);
  display.print(ina260.readPower() / 1000);
  display.print("W");
  display.setCursor(48, 40);
  display.print(" FET ");
  printTemperature(MOSFET);

  display.display();
}

void everythingOff() {  // not used yet
  digitalWrite(PANIC, LOW);
}

void printTemperature(DeviceAddress deviceAddress) {
  float tempC = sensors.getTempC(deviceAddress);
  display.print(tempC);
  display.print((char)247);
  display.print("C");

  if (tempC >= 15) {
    runFan = true;
  }
}

void controlFan() {
  {
    digitalWrite(FAN_PIN, runFan);
    if (runFan = true) {
      display.setCursor(0, 50);
      display.print("Cooling Fan On");
      display.display();
    }

    else {
      display.setCursor(0, 50);
      display.print("Things are Cool");
      display.display();
    }
  }

  controlFan();
  delay(500);
}

I have altered the code per your suggestions. I've learned quite a few things trying to figure this out and I thank you for the help and stimulus.
The "controlFan" function is not working. It's as if it's not being called. Take a look and see if I've missed something. I would like two things; I would like to make calls from the IDE to query what the values of variables are in real time and have some way to step through the code. any resources I don't know about?
Thanks, so very much for the help.
Ben

It's not.
That I can see - the only call to controlFan() is there in the function itself.

Hi there Runaway. The call is at the bottom, right above delay(500);

Yes. I agree.
What use is a call to a function that appears uniquely in that function though?

void controlFan() 
{
  {
    digitalWrite(FAN_PIN, runFan);
    if (runFan = true) {
      display.setCursor(0, 50);
      display.print("Cooling Fan On");
      display.display();
    }

    else {
      display.setCursor(0, 50);
      display.print("Things are Cool");
      display.display();
    }
  }

  controlFan();
  delay(500);
}

I think your bracing needs some review.

Ahh. Okay. It’s so easy to find documentation for functions, etc. I have had a hard time finding information about structure and the like.
Let me research how bracing isolates different parts of the code.
Thanks for the hint.
PS: “something “ is a great tune. You did a great job. Ha

A function is like a GOSUB, if you've ever worked with BASIC.

I'm not sure the intention, but following, the controlFan() function is called at the end of the loop() function --

void loop() {
  sensors.setResolution(12);
  sensors.requestTemperatures();

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);

  display.setCursor(0, 0);
  display.print("OUTPUT:");
  display.setCursor(54, 0);
  display.print("SENSOR/TEMP:");

  display.setCursor(0, 20);
  display.print(ina260.readBusVoltage() / 1000, 3);
  display.print("V");
  display.setCursor(48, 20);
  display.print(" REG ");
  printTemperature(LM317T);

  display.setCursor(0, 30);
  display.print(ina260.readCurrent() / 1000, 3);
  display.print("A");
  display.setCursor(48, 30);
  display.print(" CNV ");
  printTemperature(XL6019);

  display.setCursor(0, 40);
  display.print(ina260.readPower() / 1000);
  display.print("W");
  display.setCursor(48, 40);
  display.print(" FET ");
  printTemperature(MOSFET);

  display.display();

  controlFan();
  delay(500);
}

void everythingOff() {  // not used yet
  digitalWrite(PANIC, LOW);
}

void printTemperature(DeviceAddress deviceAddress) {
  float tempC = sensors.getTempC(deviceAddress);
  display.print(tempC);
  display.print((char)247);
  display.print("C");

  if (tempC >= 15) {
    runFan = true;
  }
}

void controlFan() 
{
  digitalWrite(FAN_PIN, runFan);
  if (runFan == true) 
  {
    display.setCursor(0, 50);
    display.print("Cooling Fan On");
    display.display();
  }
  else 
  {
    display.setCursor(0, 50);
    display.print("Things are Cool");
    display.display();
  }
}

You probably want ==, test for equality, not =, assignment.

And runFan is a boolean, you don't need to test it against true anyway. It is true or false by its very nature.


    if (runFan ) {

OIC @runaway_pancake fixed that.

a7

I don't notice runFan ever being set false except at its initial value.

a7

I don't notice runFan ever being set false except at its initial value.

At the begin of the loop you set runFan to false;

As stated in post number 4: It should be at the start of loop()

Then @benz1 hasn't yet

Furthermore, simply setting it false at the top of the loop removes or fails to provide any hysteresis, which was I thought part of the issue.

a7

100% correct, however the hysteresis is quite complex
as there is not one but three temperatures involved.

The fan went wrong as it was switched on/off per temperature sensor in the original code.

Requirement for switching ON
The fan should switch on if and as long as one of them is above its upper threshold.
This is straightforward (and basis of my proposal).

Requirement for switching OFF

  1. The fan should switch off if all of them are under the lower threshold?
  2. The fan should switch off if the sensor that switched it on is under the lower threshold?
  3. The fan should switch off if one of the sensors is under the low threshold?

OFF requirement 3 can become in conflict with the ON requirement as they can happen both "simultaneously"

Off requirement 2 can become in conflict with the ON requirement if a 2nd sensor triggers the fan.

That is why my proposal goes for the ON requirement + OFF requirement 1 as these are disjunct.

To implement the hysteresis for 3 sensors one need two flags, one to see if the ON requirement (1 above top level) is met and one to see if the OFF requirement (all below low level) is met.

1 Like

Something like this snippet, with optional different thresholds per sensor.

if one is above its highThreshold switchOn
else if all are below its lowThreshold switchOff
else keep current state

There are three options after checking the three sensors

SwitchOn SwitchOff action fan
true false HIGH
false false KEEP
false true LOW
true true can't occur
bool switchOn = false;
bool switchOff = false;

void loop() 
{
  switchOn = false;
  switchOff = true;

  sensors.setResolution(12);
  sensors.requestTemperatures();

...

  float t = printTemperature(LM317T);
  switchOn = switchOn | (t > highThreshold1);
  switchOff = switchOff & (t < lowThreshold1);

...

  t = printTemperature(XL6019);
  switchOn = switchOn | (t > highThreshold2);
  switchOff = switchOff & (t < lowThreshold2);

...

  t = printTemperature(MOSFET);
  switchOn = switchOn | (t > highThreshold3);
  switchOff = switchOff & (t < lowThreshold3);

...

  controlFan();
  delay(xxx);
}



float printTemperature(DeviceAddress deviceAddress) {
  float tempC = sensors.getTempC(deviceAddress);
  display.print(tempC);
  display.print((char)247);
  display.print("C");

  return tempC;
}


void controlFan() {
  {
    digitalWrite(FAN_PIN, runFan);
    if (switchOn) {
      display.setCursor(0, 50);
      display.print("Cooling Fan On");
      display.display();
      digitalWrite(FAN_PIN, HIGH);
    }

    if (switchOff) {
      display.setCursor(0, 50);
      display.print("Things are Cool");
      display.display();
      digitalWrite(FAN_PIN, LOW);
    }
  }
}

PS,
to "elaborate" the complexity, add a clock for if the fan is too long in the KEEP state
==> switches off to save energy.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.