|
06/22/2014, 04:29 PM | #1 |
Registered Member
Join Date: Nov 2006
Location: Virginia
Posts: 179
|
Universal Arduino Auto Top Off (ATO) Sketch Thread
Hello All,
There are many sketches out there for reef controllers but not too many of them include a robust auto top off function. My goal is to come up with the following sketch that could be added to any of the popular Arduino based reef controller sketches without issue. I have started out with a basic sketch but being more proficient with hardware than software I am asking for the communities help and input so that this goal can be reached. My ideal ATO would consist of the following: Hardware- 1) Low water level float switch 2) High water level- float witch 3) Relay to control water/air pump or solenoid. Software Side- 1) Low water float switch activates relay 2) High water level float switch activates really 3) Failsafe A: If High water relay not activated by a selectable amount of time relay is turned off 4) Failsafe B: A selectable amount of time must past in order for the ATO to be activated again 5) Failsafe C: Float switch must be activated for a selectable amount (i.e. 3 seconds) to lead to change in relay behavior (protects from temporary changes in water level 6) Must be coded without code such as delay () so that the code can be easily inserted into preexisting reef controller sketches without much issue. Below is the code I have made that covers coding issue 1) and 2). What I can not figure out is how to add the three failsafes. Code:
// constants won't change. They're used here to // set pin numbers: const int sensorPin[] = {2,3}; // the number of the float switch pins const int pumpPin = 12; // the number of the relay pin // variables will change: int sensorState = 0; // variable for reading the float switch status void setup() { // initialize the relay pin as an output: pinMode(pumpPin, OUTPUT); // initialize the floatswitch pin as an input: for(int x=0; x<2; x++) { pinMode(sensorPin[x], INPUT); } } void loop(){ // read the state of the float switch value: for(int x=0; x<2; x++) { sensorState = digitalRead(sensorPin[x]); // check if the float switch is activated. // if it is, the sensorState is HIGH: if (sensorState == HIGH && sensorPin[x] == 2) { // turn LED on: digitalWrite(pumpPin, HIGH); } if (sensorState == HIGH && sensorPin[x] == 3) { // turn relay off: digitalWrite(pumpPin, LOW); } } } Sean Last edited by dudedudedude; 06/22/2014 at 05:03 PM. |
06/23/2014, 06:14 AM | #2 |
Registered Member
Join Date: Jun 2013
Posts: 93
|
Since you are more familiar with hardware, maybe you could help by coming up with a better detector than a float switch. There are lots of horror stories of their failures. I know with the failsafes it helps eliminate them, but personally, I just don't like them.
I have a Smart ATO from AutoAqua. The thing I like about it was it uses and optical sensor and is very compact. It also comes with the pump for it is a complete unit. But one thing I did was to create an adapter on the pump's power line using a mosfet to signal my Apex when it runs. That gives me a way to log when it runs and also another failsafe. If the Apex detects it running too long, it can shut it down. Using this method, it would be fairly simple to interface it into a DIY controller to do the same thing. |
06/23/2014, 06:05 PM | #3 | |
Registered Member
Join Date: Nov 2006
Location: Virginia
Posts: 179
|
Quote:
|
|
06/23/2014, 07:16 PM | #4 |
Registered Member
Join Date: Jun 2013
Posts: 93
|
The SmartATO was more an example of alternative sensors to float switches. I had looked for something else, but nothing really stood out. I'm on the software side of things and what you are wanting is not that difficult. What you do is when one of the failsafe events happen, record the time using millis. When the event occurs again, check the current millis (milliseconds since the program started running) and see if enough time has passed. In the main loop, you can check for elapsed time since the event and act accordingly.
Now the value returned by millis will reset about every 50 days. So you will need to check if the current time is less than the start time, meaning it wrapped and have to adjust accordingly. |
06/24/2014, 07:56 AM | #6 |
Registered Member
Join Date: Oct 2004
Location: Merritt Island, FL
Posts: 726
|
I use an RTC and explicit start/stop times during the day as a fail-safe. So the fill pump can only run for 1 minute, but if the high top pops before the minute expires, the pump is stopped. Then I set 4 of these start/stop times per day.
There is a pressure sensor that Reef Angel has used to determine the level, I've been planning to replace my floats with it someday... I believe it's made by Freescale. |
06/24/2014, 11:31 AM | #7 | |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Quote:
millis() returns an unsigned long, so that's the type you should be storing these things in. With unsigned math, all you need to do is use subtraction instead of addition and the rollover works itself out for you. ie. if (currentMillis - previousMillis > interval) { // code } Here, if currentMillis is 10 because it just had a rollover and previousMillis is 4,294,967,285 (10 millisecconds away from rollover) then currentMillis - previousMillis == 10 - 4294967285 == 20 So you get the 20 milliseconds, exactly the right amount of time. Sure millis rolled over, but so did your unsigned math.
__________________
David Current Tank: Undergoing reconstruction... |
|
06/24/2014, 05:04 PM | #8 |
Registered Member
Join Date: Jun 2013
Posts: 93
|
|
06/24/2014, 06:40 PM | #9 | |
Registered Member
Join Date: Nov 2006
Location: Virginia
Posts: 179
|
Quote:
|
|
06/24/2014, 08:56 PM | #10 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
This is a pretty simple code. You've got one thing you're controlling, a pump, so let's put that on a pin and hook it to a relay. HIGH turns the pump on and LOW turns it off.
Deciding to turn the pump on requires three things to be true, the low water switch has to be down, AND it has to have been down for the three seconds, AND it has to have been enough time since the last time the pump ran. So that's one if statement with three conditions &&'ed together. Deciding to turn the pump off requires one of two things, either the high water switch is up OR the pump has been on for longer than a certain amount of time. So that's an if statement with 2 conditions ||'ed together. I split out the test for the pump running to keep the if statements from getting too clogged, but this could be done in just two or three lines if you really wanted to. Code:
byte lowWaterPin = 6; // Goes LOW when water below switch byte highWaterPin = 7; // Goes LOW when water below switch byte pumpPin = 8; // Relay to control the water pump unsigned long maxRunTime = 60 * 1000L; // pump on for max of one minute unsigned long minOffTime = 60 * 60 * 1000L; // pump must be off for at least one hour unsigned long switchDebounceTime = 3 * 1000L; // Switch must be activated for at least 3 seconds. unsigned long lastPumpTime = 0; unsigned long lastLowWaterDetectTime = 0; boolean lastLowWaterState = HIGH; boolean pumpRunning = false; void setup(){ pinMode(lowWaterPin, INPUT_PULLUP); pinMode(higheWaterPin, INPUT_PULLUP); pinMode(pumpPin, OUTPUT); } void loop(){ unsigned long currentMillis = millis; boolean lowWaterState = digitalRead(lowWaterPin); boolean highWaterState = digitalRead(highWaterPin); if(lowWaterState != lastLowWaterState){ lastLowWaterDetectTime = currentMillis; } if (pumpRunning) { // if the pump is on then let's see if we should turn it off yet if ((highWaterState == HIGH) || (currentMillis - lastPumpTime >= maxRunTime)){ digitalWrite(pumpPin, LOW); pumpRunning = false; lastPumpTime = currentMillis; } } else { // pump is not running, see if we need to turn it on if((lowWaterState == LOW) && (currentMillis - lastLowWaterDetectTime >= switchDebounceTime) && (currentMillis - lastPumpTime > minOffTime)){ // switch is low and has been for at least 3 seconds digitalWrite(pumpPin, HIGH); pumpRunning = true; lastPumpTime = currentMillis; } } lastLowWaterState = lowWaterState; }
__________________
David Current Tank: Undergoing reconstruction... Last edited by Sugar Magnolia; 06/25/2014 at 04:50 PM. |
06/25/2014, 04:26 PM | #11 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
hello all
I just found this thread and am starting to work on my ATO system code for my new double 40B display it was posted at a very good time for me!! Disc1 when i compile your sketch it gives me an error of "currentMillis' cannot be used as a function" in the second to last line of your sketch. I would like to incorporate a code like this into my sketch but also include ping sensors to do the actual monitoring of the water levels with floats as a mechanical back-up or vice versa and timers as extra back-up for system problems. could you tell me what is causing this error on compiling and possibly give me some direction as to how to incorporate the ping sensors into the sketch thanks for any help in my project James |
06/25/2014, 04:38 PM | #12 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Oops. My bad. The code was simple enough that I didn't bother to compile it and I missed a typo there.
Take the parenthesis off currentMillis. The parenthesis are for calling a function and currentMillis is a variable not a function.
__________________
David Current Tank: Undergoing reconstruction... |
06/25/2014, 04:46 PM | #13 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
thanks for your extremely fast reply to that question!
do you see a possibility that ping sensors could be incorporated into this sketch? either as the main control or the secondary back-up thanks James |
06/25/2014, 08:56 PM | #14 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
You mean a ping pointed at the water to get the level? I don't know if that works or not but it would be super easy to code if it does. Just read the ping sensor instead of a digital pin and test for the distance instead of a pin state.
__________________
David Current Tank: Undergoing reconstruction... |
06/25/2014, 10:05 PM | #15 | |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Quote:
__________________
David Current Tank: Undergoing reconstruction... |
|
06/26/2014, 05:20 PM | #16 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
i have made modifications to the sketch you posted inserting the ping sensors so far it compiles but when i try to insert what levels the ATO pump should turn on and off at i get errors
Code:
include thanks for any input James Last edited by jc286006; 06/26/2014 at 05:31 PM. |
06/26/2014, 06:14 PM | #17 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
got it to compile with the sensor values i want for a test but the led i am using to simulate a relay load will not light any suggestions were i am not doing something right
Code:
#include |
06/26/2014, 06:46 PM | #18 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Probably not your problem, but will become a problem after it runs for about 49 days...
This: Code:
if (millis() >= pingTimer[i]) Look at how I did it in the sketch a few posts up or how it is done in the glorious blink Without Delay sketch. You don't keep up with the next time it needs to run. You keep up with the last time it ran and the interval it is supposed to wait. You'll find that as you do this, a lot of the complications in your above code will disappear. Then this line: Code:
pingTimer[i] += PING_INTERVAL * SONAR_NUM;
__________________
David Current Tank: Undergoing reconstruction... |
06/26/2014, 06:59 PM | #19 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Code:
boolean lowWaterState = digitalRead(displaySensor); boolean highWaterState = digitalRead(displaySensor); You set displaySensor = cm[2] for some reason. cm[2] was zero at that point in time. So what do you have connected to pin 0? Then on this line: Code:
if ((highWaterState == displaySensor <15) || (currentMillis - lastPumpTime >= maxRunTime)) Code:
(highWaterState == displaySensor <15)
__________________
David Current Tank: Undergoing reconstruction... |
06/26/2014, 07:02 PM | #20 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Explain what you think this part does too please. echoCheck is a function. You can't use it this way. But I can't understand what you want it to do well enough to tell you how to fix it.
Code:
sonar[currentSensor].ping_timer(echoCheck); Code:
void echoCheck() { // If ping received, set the sensor distance to array. if (sonar[currentSensor].check_timer()) cm[currentSensor] = sonar[currentSensor].ping_result / US_ROUNDTRIP_CM; }
__________________
David Current Tank: Undergoing reconstruction... |
06/26/2014, 08:19 PM | #21 | |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
Quote:
OK, after going through the NewPing library a bit i get what you have going on here. I didn't realize that timer function took a callback. I thought it was supposed to take a reference to where to store the data.
__________________
David Current Tank: Undergoing reconstruction... |
|
06/26/2014, 11:30 PM | #22 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
hello i got the time roll over issue fixed per the blink without delay sketch
cm[0],cm[1],cm[2] are the three ping sensors. in a sketch i have them working to return distance values but to get them to power the led for each load simulation i plan to use them to monitor, they had to be shown like this in the code Code:
void oneSensorCycle() { // Sensor ping cycle complete, do something with the results. for (uint8_t i = 0; i < SONAR_NUM; i++) { if(cm[0] < 15) digitalWrite(ledPin1,HIGH); else digitalWrite(ledPin1,LOW); if(cm[1] < 15) digitalWrite(ledPin2,HIGH); else digitalWrite(ledPin2,LOW); if(cm[2] < 15) digitalWrite(pumpPin,HIGH); else digitalWrite(pumpPin,LOW); in the first post i hurried through it and didnt name them correctly for what i had planned on using each one for in this sketch. sorry about that!!! this sketch only deals with one of sensors jobs as i said in the first posting of code i hope that it can be expanded to encompass management of the ATO system for the display tank , the refilling of the ATO storage tank ,and the water change storeage tank. in this statement i assumed that cm[2] could be used to give a value in both states Code:
boolean lowWaterState = digitalRead(displaySensor); boolean highWaterState = digitalRead(displaySensor); Code:
(highWaterState == displaySensor <15) |
06/27/2014, 08:17 AM | #23 |
-RT * ln(k)
Join Date: Sep 2010
Location: Little Rock
Posts: 9,705
|
I'm reading your post above and it doesn't make a lot of sense to me. I'm still stuck on what exactly you think those digitalRead lines do. digitalRead reads the state of a digital pin. What you've got in that line has nothing at all to with any digital pins.
Now you've got this part: Code:
for (uint8_t i = 0; i < SONAR_NUM; i++) { if(cm[0] < 15) digitalWrite(ledPin1,HIGH); else digitalWrite(ledPin1,LOW); if(cm[1] < 15) digitalWrite(ledPin2,HIGH); else digitalWrite(ledPin2,LOW); if(cm[2] < 15) digitalWrite(pumpPin,HIGH); else digitalWrite(pumpPin,LOW); I'm starting to think maybe you don't have much experience or knowledge with C++ or with Arduino and if that is the case then this project is probably a bit over your head. Go back through some examples and make LED's flash and read some simple buttons and such. Learn what the commands like digitalRead and digitalWrite do and learn how for loops and arrays actually work. Once you have that under your belt, this code will be trivial.
__________________
David Current Tank: Undergoing reconstruction... |
06/27/2014, 11:17 AM | #24 |
Registered Member
Join Date: Oct 2006
Location: ramsey illinois
Posts: 218
|
hello david
No I don`t know C++ all I know is what I have read on the internet about arduino and its code in tutorials I am not schooled in programming I am not a dumb person just not taught in the correct way of doing things. The bit of code I posted was part of the 15 sensor ping example that is in the new ping library. I got that sketch modified to return values for the 3 ping sensors I am using and lighting leds to simulate the load that I want to control but as you can see it only senses the high/full water level. The modifications I posted above to the 15 sensor example sketch were from suggestions from people at the arduino forum. I was hoping to incorporate the sketch you posted in this thread and the new ping sketch together to use each of the 3 sensors to control not only the high/full water level but to also control when a pump for the ATO and the solenoid controlling my RO/DI filter would turn on at a certain lower level and run for so many minutes until the upper limit was met so that they were not pulsing on and off at short intervals.I will read more at the arduino site and try everything i can learn before posting code for help thanks james |
06/27/2014, 11:47 AM | #25 |
Registered Member
Join Date: May 2010
Location: Decatur, IL
Posts: 1,048
|
i like the ping sensor idea! Anyone tried this in a sump before?
|
|
|