Aquarium controller with a debounce on the temperature sensor

Hello, this is my first time posting to an online forum such as this although I have always browsed other peoples posts. Please excuse me if I do not follow the modern etiquette of posting as I will learn over time.

My current project has a ds1820b temperature sensor and a button as well as a 2 module relay to control the heater and pump for my aquarium. I also am using a 16 x 2 LCD to show the current selected target temperature along with the actual water temperature at the same time.

I use 12v into Vin on the nano directly from the power supply plugged into the wall. Then I put a 7508 to regulate the 12v down to 5v which powers the 2 relay module. The temp sensor pulls its 5v directly from the 5v output on the nano with a 4.7k resistor between the data line and 5v.

When it comes to the float switches, I have them wired in series, one switch at NC and the main switching float as NO. The idea is that if the first switch fails and welds shut to NC, the second switch will be a backup breaking the circuit. I have considered allowing the 5v powering the relay module to flow thru the NC switch so when that is triggered, it will literally power down the relay module rather then using them is series for only signal control.
On the AC side of things, both the AC components are hooked up to the NO side of each relay. The pump required a snubber so it is made of a .1uf cap, and a 470 ohm resistor.

What my problem is I have always had trouble understanding the debounce example for some reason. Right now I basically have it check the temp sensor every 15 seconds because if I do not do that the relay for the heater will bounce up and down as the water cools and it is on the edge of the setpoint reading.

What I really want is the temp sensor to be able to read at its regular interval based on the set resolution and only turn the heater on when the sensor value is below the setpoint for lets say 5-20 seconds. In its current programmed state, if I remove the sensor from the water it will not update the display for 15 seconds of course which I want immediate updating but delayed relay triggering. The same debounce could be used for the float switch because right now its simply "Do not check for state change on the float for 1.5 seconds" and in that case, sometimes when a wave bounces the switch the state change check could be at that time and the relay flips. What it should be is when the state changes it must change for 1.5 seconds before the relay pin goes low.

I began to use the debounce a button example but the example assumes, I am using a button to control the ledstate. I would appreciate if someone could help shed some light on how to transform the debounce into a temperature and float debounce so I could understand it a little better. I have posted my code below

/*temperature and ato controller with lcd display for changing the temp setpoint with
  2 buttons for lcd control. the relay when high = pump or heater off. when low the pump and heater are on using a 2 relay module from amazon.*/


#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const int heater = 3; //pin for 5v relay
const int pump = 4; //pin for 5v relay
const int floatSwitch = 5; //main waterlevel sensor

//temp screen control variables
const int buttonPin = 6;    // the pin that the pushbutton is attached to
float setPoint = 76.00;   // counter for the number of button presses
//int heaterState = HIGH;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;
//unsigned long lastDebounceTime = 0;
//unsigned long debounceDelay = 1000;
unsigned long previousMillis = 0;
unsigned long previousMillis2 = 0;
const long interval = 15000;
const long interval2 = 1500;


// Data wire is plugged into pin 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

LiquidCrystal_I2C lcd (0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display

void setup(void)
{

  lcd.init();  // initialize the lcd
  lcd.backlight();  //turn on the backlight
  pinMode(heater, OUTPUT);
  pinMode(pump, OUTPUT);
  digitalWrite (pump, HIGH);
  digitalWrite (heater, HIGH);
  pinMode(floatSwitch, INPUT_PULLUP); //using the pullup
  pinMode(buttonPin, INPUT_PULLUP);
  sensors.begin();  //turn on the temp sensor
  sensors.setResolution(12);  //set temp tolerance
  Serial.begin(9600);

}

void loop(void)
{

  int waterLevel = digitalRead(floatSwitch);
  int buttonState = digitalRead(buttonPin);
  unsigned long currentMillis = millis();
  unsigned long currentMillis2 = millis();

  if (buttonState != lastButtonState) {
    if (buttonState == LOW) {
      setPoint += .25;
    }
  }

  lastButtonState = buttonState;

  if (setPoint > 85.00) {
    setPoint = 72.00;
  }

  lcd.setCursor(0, 0);
  lcd.print("Set:");
  lcd.setCursor(11, 0);
  lcd.print(setPoint);
  lcd.setCursor(0, 1);
  lcd.print("Actual:");
  lcd.setCursor(11, 1);
  lcd.print(sensors.getTempFByIndex(0));

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    sensors.requestTemperatures();

    if (sensors.getTempFByIndex(0) < setPoint) {
      digitalWrite(heater, LOW); //turn on the heater relay
    } else {
      digitalWrite(heater, HIGH); //otherwise turn off the relay
    }
  }

  if (currentMillis2 - previousMillis2 >= interval2) {
    // save the last time you blinked the LED
    previousMillis2 = currentMillis2;

    if (waterLevel == HIGH) { //turn on the pump at this value
      digitalWrite(pump, LOW);
    } else {
      digitalWrite(pump, HIGH); // otherwise turn off the pump normally
    }
  }
}

It usually makes more sense to use hysteresis (a dead zone) with a thermostat, rather than delays.

So if I understand correctly, I would code it as:

if the actual temperature < then 76, write high until the temp is reached at 77 for example. Although the setpoint maybe selected at 76, using your method it would actual then take the temperature up to 77. It doesn't matter if the temp read goes to 75.99 for a second, it will turn on until it reached the upper set point every time.

The way I am thinking that should in fact keep it from bouncing.

You do not "debounce" a temperature sensor. As aarg wrote, use hysteresis. For example, if the setpoint is 15 (degrees C), turn the heater on at 14 (or below) and turn the heater off at 16 (or above). The "deadzone" in this example is 2 degrees C. This example assumes of course that the environment is working to cool the water when the water is within a few degrees of the setpoint else a cooler and more complex logic is needed.

Yes thanks everyone, I didn't understand it at first but I do now. It is definitely the way to go for the temp sensor.

How do I handle the float? Is what I have posted for timing the checking of the float state appropriate or is there another way to handle the float?

JayMan:
Yes thanks everyone, I didn't understand it at first but I do now. It is definitely the way to go for the temp sensor.

How do I handle the float? Is what I have posted for timing the checking of the float state appropriate or is there another way to handle the float?

It depends on what your aim is. How do you want the pump to behave, exactly?

When a wave makes the float go up and down, I do not want the pump to trigger. My aim is when the float is moved to the opposite state of normal, it must be there for a set amount of time before the relay is activated to turn the pump on.

What I have right now is a basically a timer which at the set interval, checks the state of the float. Sometimes that interval is just about up when the float is activated and it maybe .25 seconds before the pump activates this time, and it maybe 2.75 seconds before it activated that time rather then the full amount of the interval each time there is a state change.

Pseudo code:

if float == LOW

wait 3 seconds and the float is still LOW

digitalWrite float low

else keep float high

vs what I have

wait 3 seconds and check the float

if float == LOW

digitalWrite float low

else keep float high

JayMan:
When a wave makes the float go up and down, I do not want the pump to trigger. My aim is when the float is moved to the opposite state of normal, it must be there for a set amount of time before the relay is activated to turn the pump on.

Then sample more often and count "active" readings. Reset the count any time an "inactive" reading occurs. Trigger the pump and change the state whenever the count exceeds a threshold.

Rule 1: Do NOT use delay(...) or you will lose the ability to check on other things while the delay is timing out.

Rule 2:
Each time through the loop() function, check the state of the float.
If the float is normal, reset a timer variable (declared as unsigned long) to the current time given by the millis() function, else determine whether a sufficient interval (declared const unsigned long) has passed (greater than or equal to).
If sufficient time has passed, activate the pump, else shut the pump off. (Shutting the pump off if the pump is already off should be harmless.)

This scheme only activates the pump if the float is NOT in the normal state for a sufficient interval of time. If the float goes into the normal state during the interval, the interval starts fresh. Once the pump gets activated, it will stay activated until the float gets into the normal state. The only problem I foresee is that the float could get back into the normal state either due to the pump starting up or due to waves. Eventually, you may want to insist that once the pump gets activated, it stays activated unless the float is in the normal state for a sufficient interval. Come back to us for further help if you want it.

I should have pointed out two things:

(1) All variables having to do with time should be unsigned long [because that is what millis() returns].
(2) Only use subtraction. Addition will fail every 49 days or so when calculating time via millis(). Subtraction ALWAYS works.

aarg suggested using a count. This will work but you will have to figure out a good value for the count. Instead of a count, I suggested a time. This will work and does not require figuring out a count but might be a bit harder to think about. You choose.

I would rather not go the count route on this one. How do I write the code so it works as you have described for the float vaj?

Do I put the currentmillis - previousmillis withing the waterlevel == high if statement?

What have you tried so far? I would think that my reply #8 would be sufficient to start coding but I could be wrong.

If you are stuck, I suggest that you read Gammon Forum : Electronics : Microprocessors : millis() overflow ... a bad thing? and Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs.

Good Luck!

I am having trouble thinking of how to do they hysteresis, am I on the right track?

I am changing the state of the heater and digitalWriting that state until the else if tells it to change to the opposite state after hitting the mark of setpoint + a specified number

hysteresis = setPoint + .50;

sensors.requestTemperatures();

if (sensors.getTempFByIndex(0) < setPoint) {
heaterState = HIGH;
} else if (sensors.getTempFByIndex(0) >= hysteresis) {
heaterState != heaterState;
}

digitalWrite(heater, heaterState);

That is the correct idea but it is hard to help when I do not know how hysteresis was declared, how setPoint was declared, how heaterState was declared, or what getTempFByIndex(0) returns.

Assuming that hysteresis and setPoint were declared float or double, and assuming that heaterState is declared int, and assuming that getTempFByIndex(0) returns a float or a double, then I might be tempted to write:

hysteresis = setPoint + .50;

sensors.requestTemperatures();
float temperatureF = sensors.getTempFByIndex(0) 

  if (temperatureF < setPoint) {
    heaterState = HIGH;
  } else if (temperatureF >= hysteresis) {
    heaterState = LOW;
  }

  digitalWrite(heater, heaterState);

but that just makes the heaterState more obvious and avoids getTempFByIndex(0) doing whatever it does twice. Your code snippet probnably works (depending upon whatever the rest of the code that I can't see is).

By the way, hysteresis is usually the difference (or one half the difference) between the upper setpoint and the lower setpoint, so the variable is misnamed. The 0.50 value that you use is the true hysteresis.

If you declared your variable hysteresis as an int or a long, you are going to have a problem.

Before you post any more code snippets, please see this.

Yes that looks like a better way to write it. Thanks