Go Down

Topic: Using float switches with safeties (Read 2636 times) previous topic - next topic

Sleepydoc

Re: temperature - No, the fish are not that sensitive. They actually need stability more than accuracy. Corals more than fish, if you're keeping corals, too. As far as the thermometers, the big concern with aquarium thermometers is that their thermostats tend to fail in the on position. This is eliminated by using the arduino as a thermostat. After that, cycling on and off is the next big culprit. You need to build some hysteresis in your code so the heater turns on at say 76º F, and then kicks off at 77º F.

I'm working on a similar project, below is how I coded the logic for the heater:

Code: [Select]

        //  ********Heat Outlet ********
        if (fTemp < heatOnTemp && fTemp > 32) Outlets[Heater1Pin] |= 1;    //Heater on unless ftemp <32(temp probe absent)
        else if (fTemp > heatOnTemp+1) Outlets[Heater1Pin] &= ~1;  // turn off at onTemp + 1

Sleepydoc

1 for turning on and off the pump(floatswitch1), another for a safety on that switch so I don't over fill(floatswitch2), 1 in the water reservoir so the pump won't run dry(floatswitch3), and a fourth as a reservoir fill indicator(floatswitch4)


I can't understand exactly what you're trying to control with what. 4 switches in how many locations controlling how many pumps? I'm assuming you have a sump and an ATO reservoir. Is switch 1 for the sump level to turn the ATO pump on/off, then pump 2 a 'failsafe' to turn it off if the sump gets over filled?  It sounds like switch 4 is only to indicate the level of the ATO reservoir, so that is independent of the rest, correct?

+1 to Robin2's suggestion of giving each it's own variable. Then try making a logic diagram to help clarify what you need to do, something like:
Switch 1  Switch 2  Switch 3  Output 
000Off
001On
010Off
… etc

GingerPolarBear

If the spec is ±.5 deg C, I'll take .18 deg F any day of the week.

Quote
4 switches in how many locations controlling how many pumps?

1 pump in the reservoir, 2 switches in the sump, 2 switches in the res.
Quote
Is switch 1 for the sump level to turn the ATO pump on/off, then pump 2 a 'failsafe' to turn it off if the sump gets over filled?

Yes. Exactly. (assuming you meant switch 2, not pump 2)
Quote
It sounds like switch 4 is only to indicate the level of the ATO reservoir, so that is independent of the rest, correct?

Yes. I'm only using that as an indicator to fill the res.

Switch 3 is also a 'failsafe', located near the bottom of the reservoir to keep the pump from running dry.

I'm thinking it might be better using AND and OR statements for this. And, giving each switch it's own buttonState. Something like this, maybe?
Code: [Select]

int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;


buttonState1 = digitalRead(floatswitch1);
buttonState2 = digitalRead(floatswitch2);
buttonState3 = digitalRead(floatswitch3);
buttonState4 = digitalRead(floatswitch4);

if(buttonState1 == LOW && buttonState3 == HIGH){ 
        digitalWrite(relay4, LOW);
          delay(buttonState1 == High || buttonState2 != LOW); 
}
else{
        digitalWrite(relay4, HIGH);
}
         

Grumpy_Mike

Quote
If the spec is ±.5 deg C, I'll take .18 deg F any day of the week.

As I said before don't confuse resolution with accuracy.
Accuracy is that if you get a temperature reading that temperature reflects the true temperature to within  ±.5 deg C. That applies whether you are measuring in C or F.
Resolution is how fine that temperature is expressed. So you might have a reading of 19.0, 19.1, 19.2, 19.3 and so on. Given that you have monotonicity then 19.1 is always going to be hotter that 19.0. However, the true temperature with a reading of 19.0 can be anywhere between 18.5 and 19.5, likewise with a reading of 19.1, the true temperature can be anywhere between 18.6 and 19.6.
But the difference between the true temperature and your reading will always be the same offset. So you can calibrate your one sensor with a known temperature and find the offset then apply that to your reading to get an accuracy that matches the resolution.

Sleepydoc

#19
Feb 06, 2014, 09:19 pm Last Edit: Feb 06, 2014, 10:22 pm by Sleepydoc Reason: 1
You shoot 10 arrows at a target, then try to figure out where the center of the target is based on the holes:
  • Accuracy = how close they are to the center of the target. Can potentially be corrected for if you had a more accurate measure to calibrate against.

  • Reliability = how consistent measurements are for a given value (~ how tight the grouping is)

  • Precision = how fine a measurement you can make (think size of the holes)

  • Resolution = the ability to discriminate between different two different values with different measurements (related to reliability)


You can see how they are all related but distinct components of measuring things. If a measurement is precise to 0.00001º, but varies by +/- 2º with each measurement, the precision is meaningless.x Conversely, if it is highly reliable/consistent but there is only a 2 degree resolution you will never get much better.

That said, I find it much easier to deceive myself that they're all the same!  XD

Regarding the logic for your float switches and pumps, this is what I came up with:
Code: [Select]
1 2 3 Output
0 0 0 0
0 0 1 0
0 1 0 0
0 1 1 0
1 0 0 1
1 0 1 0
1 1 0 0
1 1 1 0


i.e. the only time you want the ATO pump on is if the sump level switch indicates a low level AND the high-level switch is off AND the low-level switch in the reservoir is off. Code for this should be something like:

if (A == 1 && B==0 && C==0) {
 <turn pump on>
}

The 4th float switch is just a indicator that the level is low enough to add water, but not that you need to turn off the ATO pump, correct? If so, it is completely independent of the others.

As an aside, consider using something like this http://www.parallax.com/product/28015?SortField=ProductName,ProductName or this http://www.amazon.com/SainSmart-HC-SR04-Ranging-Detector-Distance/dp/B004U8TOE6/ref=sr_1_1?ie=UTF8&qid=1391718298&sr=8-1&keywords=sainsmart+ultrasonic to measure the water level in the ATO tank. I'm making  a controller and will have one mounted at the top measuring distance to the water level, then displaying a % of how full the tank is.

GingerPolarBear

Quote
if (A == 1 && B==0 && C==0) {
  <turn pump on>
}


This is exactly what I ended up doing (about an hour before you posted this  :) ) and it works like a charm. Thanks!

As far as the temperature readings go, I can live with what I'm getting. However,  I would like to add some kind of time delay for when things turn off and on. For example:

if(tempAdjust<77.80 [for this long]){
    digitalWrite(relay3, LOW);
        delay(tempAdjust==88.30 [for this long]);

How would I go about doing this? I'm sure there would need to be more to the code than just this, just trying to be understood. I'm to understand that a delay() stops the entire loop. I only want this particular action to have a delay, not the entire loop. I'm not exactly sure that an interrupt is what I'm looking for either. I have an RTC coming soon, but, again, I'm not sure if that will help solve this or not.

Sleepydoc


As far as the temperature readings go, I can live with what I'm getting. However,  I would like to add some kind of time delay for when things turn off and on. For example:

if(tempAdjust<77.80 [for this long]){
    digitalWrite(relay3, LOW);
        delay(tempAdjust==88.30 [for this long]);

How would I go about doing this? I'm sure there would need to be more to the code than just this, just trying to be understood. I'm to understand that a delay() stops the entire loop. I only want this particular action to have a delay, not the entire loop. I'm not exactly sure that an interrupt is what I'm looking for either. I have an RTC coming soon, but, again, I'm not sure if that will help solve this or not.


No, a RTC will not help for what you want to do; it's only useful for setting time variables to keep track of hours, minutes, etc. (you can do this internally, but the Arduino's internal clock isn't very accurate.)

You are right, the delay function will work for what you want, but prevents the Arduino from doing anything else in the mean time. For what you want, look at the blink without delay example sketch. Arduino has an internal timer that you can read with the millis() command. Essentially, your sketch would be something like:

if (temp < triggerTemp && lowTempFlag = 0) { 
  tempTriggerTime = millis();  // time to start counting
  lowTempFlag = 1;  //flag to indicate you're started counting
} else {
  lowTempFlag = 0;
}

if (lowTempFlag == 1 && (mills() - tempTriggerTime) > delayPeriod){
  <turn on heater>
}

 

retrolefty

Quote

Reliability = how consistent measurements are for a given value (~ how tight the grouping is)


In the petro/chemical industrial instrumentation world, we and most of our equipment vendors
use the word Repeatablity instead of Reliability. Same concept different jargon.

Accuracy in measurements gets more complex the more you expect from it.


GingerPolarBear

Quote

if (temp < triggerTemp && lowTempFlag = 0) { 
  tempTriggerTime = millis();  // time to start counting
  lowTempFlag = 1;  //flag to indicate you're started counting
} else {
  lowTempFlag = 0;
}

if (lowTempFlag == 1 && (mills() - tempTriggerTime) > delayPeriod){
  <turn on heater>
}


I used this for the fan section and it didn't work. Here's the whole section of code for the fan.
Code: [Select]


long fanTriggerTime;

      lcd.setCursor(0,3);   
         lcd.print("Fan:");
         
float highTempFlag = 0;
float deadband = 0.72;

      if (temp >79.00 && highTempFlag == 0){
  fanTriggerTime = millis();  // time to start counting
  highTempFlag = 1;  //flag to indicate you're started counting
}
else {
       highTempFlag = 0;
}

if (highTempFlag == 1 && (millis() - fanTriggerTime) > 30000){
    digitalWrite(relay2, LOW);
          lcd.setCursor(4,3);
              lcd.print("On ");
}
else{
       if (temp < (79.00 - deadband)){
          digitalWrite(relay2, HIGH);

              lcd.setCursor(4,3);
                lcd.print("off");
     }
}

I'm not sure what I did wrong, if anything, but there is no delay in relay2 coming on and it causes the lcd to not print "On" for the fan. Adding the same timing code to the off function causes all of the lcd variables to not print and relay2 to continuously click off and on.

wildbill

Using a float for a flag isn't a very good idea - comparing a float to a constant is dodgy at best - they need wiggle room. Use a boolean, or at least an int. Post all your code - too hard to help from snippets.

michinyon

Quote
I would like this to be much more accurate.


This is probably an unrealistic expectation.

michinyon

Quote
Currently, there is a .16 gap


The reason for this,  is that it is creating a result with a resolution of 0.1 Celsius degrees    and you are then converting this to your curious antique american customary units.

GingerPolarBear


Quote
Currently, there is a .16 gap


The reason for this,  is that it is creating a result with a resolution of 0.1 Celsius degrees    and you are then converting this to your curious antique american customary units.

Yep. More accuracy is definitely unrealistic. If I weren't converting this to our curious antique american customary units it would work better, but I'm not a trend setter. I can live with how the temp reports.


Using a float for a flag isn't a very good idea - comparing a float to a constant is dodgy at best - they need wiggle room. Use a boolean, or at least an int. Post all your code - too hard to help from snippets.

I'll try an int and see how it goes. Coding C/C++ is new to me. I learned html almost 2 decades ago, and forgot how to do most of it. The logic makes sense, but I don't even know where to start on a boolean.
Here's the entire code:
Code: [Select]

                            //////LIBRARIES/////// 
                           
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <OneWire.h>   

                     
                            ///////DATA TYPE///////
                           
int DS18S20_Pin = 10;                 //DS18S20 Signal pin on digital 10
OneWire ds(DS18S20_Pin);              //Temperature chip i/o on digital pin 10
int floatswitch1 = 38;
int floatswitch2 = 40;
int floatswitch3 = 42;
int floatswitch4 = 44;
int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int buttonState4 = 0;
//#define relay1 30
#define relay2 32
#define relay3 34
#define relay4 36
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);




void setup(){
 
   Serial.begin(9600);  // Used to type in characters

   lcd.begin(20,4);         // initialize the lcd
 
     // pinMode(relay1, OUTPUT);
      pinMode(relay2, OUTPUT);  //Fan
      pinMode(relay3, OUTPUT);  //Heater
      pinMode(relay4, OUTPUT);  //ATO
      pinMode(floatswitch1, INPUT);  //fill switch
      pinMode(floatswitch2, INPUT);  //over-fill safety
      pinMode(floatswitch3, INPUT);  //resevior pump safety
      pinMode(floatswitch4, INPUT);  //resevoir low indicator
      digitalWrite(floatswitch1, HIGH);
      digitalWrite(floatswitch2, HIGH);
      digitalWrite(floatswitch3, HIGH);
      digitalWrite(floatswitch4, HIGH);
     
    }

void loop(void){
 
                                 ///////ONE_WIRE TEMP///////
                                 
     float temperature = getTemp();
     Serial.println(temperature);

     //delay(1000);   
    }


   
     float getTemp(){                       //returns the temperature from one DS18S20 in DEG Celsius
   
        byte data[12];
        byte addr[8];
     
      if (!ds.search(addr)){               //no more sensors on chain, reset search
          ds.reset_search();
          return -1000;
     }
   
      if (OneWire::crc8( addr, 7) != addr[7]) {
          Serial.println("CRC is not valid!");
          return -1000;
     }
   
       if (addr[0] != 0x10 && addr[0] != 0x28) {
           Serial.print("Device is not recognized");
           return -1000;
     }
   
       ds.reset();
       ds.select(addr);
       ds.write(0x44,1);            // start conversion, with parasite power on at the end
   
       byte present = ds.reset();
       ds.select(addr); 
       ds.write(0xBE);              // Read Scratchpad
     
     
       for (int i = 0; i < 9; i++) {         // 9 bytes
            data[i] = ds.read();
     }

       ds.reset_search();

       byte MSB = data[1];
       byte LSB = data[0];
   
       float tempRead = ((MSB << 8) | LSB);
       float TemperatureSum = ((tempRead *9/5)+32)/10;
       float temp = (TemperatureSum *.998);
       
                        ////// Temp sent to LCD////////
   
       lcd.setCursor(0,1);      //Start at character 0 on line 0
         lcd.print(temp);     //output the temperature to lcd
         lcd.print("  ");
       lcd.setCursor(5,1);
         lcd.print((char)223);      //degrees symbol
       lcd.setCursor(6,1);
         lcd.print("F");
   
   
   
                                     ///////Heater///////
   
       lcd.setCursor(0,2);
          lcd.print("Heat:");
         
       if(temp < (78.28 - deadband)){
          digitalWrite(relay3, LOW);
       
             lcd.setCursor(5,2);
               lcd.print("On ");
     }
       else{
            if(temp > 78.28);
               digitalWrite(relay3, HIGH);

              lcd.setCursor(5,2);
                lcd.print("off");
     }
       
       
                          ////////Fan/////////                 
       
   
long fanTriggerTime;

      lcd.setCursor(0,3);   
         lcd.print("Fan:");
         
int highTempFlag = 0;
float deadband = 0.72;

      if (temp >79.00 && highTempFlag == 0){
  fanTriggerTime = millis();  // time to start counting
  highTempFlag = 1;  //flag to indicate you're started counting
}
else {
       highTempFlag = 0;
}

if (highTempFlag == 1 && (millis() - fanTriggerTime) > 30000){
    digitalWrite(relay2, LOW);
          lcd.setCursor(4,3);
              lcd.print("On ");
}
       else{
           if(temp < 78.28);
              digitalWrite(relay2, HIGH);

              lcd.setCursor(4,3);
                lcd.print("off");
     }
     
         
                                //////////////ATO///////////////////
     
        lcd.setCursor(11,1);
          lcd.print("Sump:");
        lcd.setCursor(11,2);
          lcd.print("Rsvr:");
               
         buttonState4 = digitalRead(floatswitch4);
      if(buttonState4 == LOW && buttonState3 != LOW){
       
         lcd.setCursor(16,2);
           lcd.print("*Low");
     }
       else{
           lcd.setCursor(16,2);
             lcd.print("Okay");
     }
           buttonState3 = digitalRead(floatswitch3);
        if(buttonState3 == LOW && buttonState4 == LOW){
         
              lcd.setCursor(16,2);
                lcd.print("MPTY");
                    delay(400);
              lcd.setCursor(16,2);
                lcd.print("    ");
                    delay(200);
      }
            buttonState1 = digitalRead(floatswitch1);
            buttonState2 = digitalRead(floatswitch2);
            buttonState3 = digitalRead(floatswitch3);
         if(buttonState1 == LOW && buttonState3 == HIGH && buttonState2 == LOW){  //pump on
            digitalWrite(relay4, LOW);
                   
               lcd.setCursor(16,1);
                 lcd.print("Fill");
      }
       else{
            digitalWrite(relay4, HIGH);  //pump off
       
              lcd.setCursor(16,1);
                lcd.print("Okay");
      }
     
     
     
     
      }/////////////////////////////END////////////////////////



Sleepydoc

#28
Feb 09, 2014, 11:53 pm Last Edit: Feb 10, 2014, 12:10 am by Sleepydoc Reason: 1

Using a float for a flag isn't a very good idea - comparing a float to a constant is dodgy at best - they need wiggle room. Use a boolean, or at least an int. Post all your code - too hard to help from snippets.


Not the best practice, and a waste of memory, but it should work fine.

+1 on posting the entire code. THere may be something somewhere else that explains the problem. Also, use Auto Format in the Arduino IDE - makes it easier to read.

Problems with the code I can see off hand:
1. are the variables defined as printed, or outside the main loop?
2. Temp goes above 79 and flag is 0, so the flag is set and triggerTime=millis(); Next time through the loop if temp > 79 and Flag is 1, it goes to the 'else' portion where it sets Flag=0 (I realize you copied my example and thus the mistake from it :smiley-eek-blue:)

These don't explain why the relay comes on right away, but the fact that it doesn't print 'on' says that the fault may be somewhere else. You need to start adding 'serial.print' statements to debug.

If your goal is simply to prevent excessive cycling of the heater/fan, an easier method would be to have the 'on' temperature different from the 'off' temperature i.e have the fan come on at 79 degrees but shut off at 78 degrees. This will be easier to program should have the same effect. You can also base the decision off a running average of the temperatures; this would make it a bit less susceptible to small variations.

EDIT:
Saw the post you made while I was typing; the 'long fanTriggerTime' definition inside the main loop will re-initialize each time the loop is run unless it's declared static. Run the following code, then comment out the 'int y' line and un-comment the 'static int y' and compare. Also, if you're not going to change variables (like deadband,) you can use #define at the beginning of your code and save memory.

Code: [Select]
int x = 0;

void setup(){
  Serial.begin(57600);
}

void loop(){
  int y;
  // static int y;
  x++;
  y++;
 
  Serial.print("x = ");
  Serial.print(x);
  Serial.print("  y = ");
  Serial.println(y);
  delay(1000);
}

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy