Pool automation beginnings

Through multiple internet searches, I have come across thermistor based set-ups. Most everything I found were all basically flavors of the same base code. What I need is slightly different, as I wanted 4 probes, as follows:

Pool temperature
Heater inlet (you'd think this wouldn't be different from the pool body, bu it does differ slightly.)
Heater outlet -- for checking the delta t, as my heater is an experimental unit
Pump windings -- basically a motor health check.

Eventually I'd like to add in an electrical draw as well for the pump, along with timer and relay control for it. I've also got the hardware for a chlorine dosing pump I need to add to the mix. The dosing pump will be tied in to the pump run times, to ensure that I don't end up with a "hot spot" of liquid chlorinator. I don't think adding an RTC module should be that hard, there are many examples available for that.

Long term plans include nRF24L01 transceivers for logging purposes, along with LCD readouts in the house.

What I've done so far to get what I need is this:

#include <math.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd (2,3,4,5,6,7); /* 2=rs; 3=enable; 4=D4; 5=D5; 6=D6; 7=D7 */

double Thermistor1(int RawADC) {
 double TempA;
 TempA = log(10000.0*(1024.0/RawADC-1)); 
//         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
 TempA = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * TempA * TempA ))* TempA );
 TempA = TempA - 273.15;            // Convert Kelvin to Celcius
 TempA = (TempA * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return TempA;
}

double Thermistor2(int RawADC) {
 double TempB;
 TempB = log(10000.0*(1024.0/RawADC-1)); 
//         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
 TempB = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * TempB * TempB ))* TempB );
 TempB = TempB - 273.15;            // Convert Kelvin to Celcius
 TempB = (TempB * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return TempB;
}

double Thermistor3(int RawADC) {
 double TempC;
 TempC = log(10000.0*(1024.0/RawADC-1)); 
//         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
 TempC = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * TempC * TempC ))* TempC );
 TempC = TempC - 273.15;            // Convert Kelvin to Celcius
 TempC = (TempC * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return TempC;
}

double Thermistor4(int RawADC) {
 double TempD;
 TempD = log(10000.0*(1024.0/RawADC-1)); 
//         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
 TempD = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * TempD * TempD ))* TempD );
 TempD = TempD - 273.15;            // Convert Kelvin to Celcius
 TempD = (TempD * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return TempD;
}

void setup() {
 //Serial.begin(115200);
 lcd.begin(20,4);
}

void loop() {

 lcd.setCursor(0,0);
 lcd.print("Pool:");
 lcd.setCursor(12,0); 
 lcd.print(int(Thermistor1(analogRead(0))));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,1);
 lcd.print("Heater in:");
 lcd.setCursor(12,1); 
 lcd.print(int(Thermistor2(analogRead(1))));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,2);
 lcd.print("Heater out:");
 lcd.setCursor(12,2); 
 lcd.print(int(Thermistor3(analogRead(2))));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,3);
 lcd.print("Pump:");
 lcd.setCursor(12,3); 
 lcd.print(int(Thermistor4(analogRead(3))));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");
 
 delay(1500);

 lcd.clear();
}

Being still very new to arduino and coding in general, I can't help but think this could be improved somewhat. I'm sure that if I were able to code directly in c++, it could be done.

This build so far is working on a pro mini with a 20x4 character LCD. When compiled, results are:

 Sketch uses 5,052 bytes (16%) of program storage space. Maximum is 30,720 bytes.
 Global variables use 81 bytes (3%) of dynamic memory, leaving 1,967 bytes for local variables. Maximum is 2,048 bytes.

I'm not looking for a handout, just some helpful hints how I could do it better. I'll learn quicker if I can be pointed in the right direction, so I can figure out the wherefores, if not the why's.

You don't need to repeat the exact same function 4 times.
Just have one, send the respective ADC reading and print (or whatever) the result.

Edit: Like this:-
(I haven't checked anything else. I don't have the original "LiquidCrystal" library to compile this code.)

#include <math.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd (2, 3, 4, 5, 6, 7); /* 2=rs; 3=enable; 4=D4; 5=D5; 6=D6; 7=D7 */

double Thermistor(int RawADC)
{
    double Temp;
    Temp = log(10000.0 * (1024.0 / RawADC - 1));
    //         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
    Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp )) * Temp );
    Temp = Temp - 273.15;            // Convert Kelvin to Celcius
    Temp = (Temp * 9.0) / 5.0 + 32.0; // Convert Celcius to Fahrenheit
    return Temp;
}

void setup()
{
    //Serial.begin(115200);
    lcd.begin(20, 4);
}

void loop()
{
    lcd.setCursor(0, 0);
    lcd.print("Pool:");
    lcd.setCursor(12, 0);
    lcd.print(int(Thermistor(analogRead(0))));
    lcd.print(" ");
    lcd.print((char)223);
    lcd.print("F ");

    lcd.setCursor(0, 1);
    lcd.print("Heater in:");
    lcd.setCursor(12, 1);
    lcd.print(int(Thermistor(analogRead(1))));
    lcd.print(" ");
    lcd.print((char)223);
    lcd.print("F ");

    lcd.setCursor(0, 2);
    lcd.print("Heater out:");
    lcd.setCursor(12, 2);
    lcd.print(int(Thermistor(analogRead(2))));
    lcd.print(" ");
    lcd.print((char)223);
    lcd.print("F ");

    lcd.setCursor(0, 3);
    lcd.print("Pump:");
    lcd.setCursor(12, 3);
    lcd.print(int(Thermistor(analogRead(3))));
    lcd.print(" ");
    lcd.print((char)223);
    lcd.print("F ");

    delay(1500);

    lcd.clear();
}

Thank you for your reply, OldSteve.

Perhaps I should have explained a bit more about the code as originally written.

I had left it that way initially, just as you have written. I noticed a problem with it almost immediately after loading it. For some reason that I can't quite get my head around, any change from any probe tends to be reflected in the rest of the readings. I verified this using ice water and a teakettle. One probe in ice water, one probe in tap water, and a third in the teakettle. As the water in the kettle got warmer, nicely showing on it's probe, the other probes also followed, at varying rates. When I separated the function out to each probe, the tendency disappeared.

I suspect it has something to do with residuals from each calculation still being in the registers used for them skewing the results. I don't know, it just seems odd that the probe results would do this. note: it also did the same thing when I removed the probe from any of the containers. as one reading changed, so did all the others.

C_Raynor:
Thank you for your reply, OldSteve.

Perhaps I should have explained a bit more about the code as originally written.

I had left it that way initially, just as you have written. I noticed a problem with it almost immediately after loading it. For some reason that I can't quite get my head around, any change from any probe tends to be reflected in the rest of the readings. I verified this using ice water and a teakettle. One probe in ice water, one probe in tap water, and a third in the teakettle. As the water in the kettle got warmer, nicely showing on it's probe, the other probes also followed, at varying rates. When I separated the function out to each probe, the tendency disappeared.

I suspect it has something to do with residuals from each calculation still being in the registers used for them skewing the results. I don't know, it just seems odd that the probe results would do this. note: it also did the same thing when I removed the probe from any of the containers. as one reading changed, so did all the others.

That is indeed strange. I don't have any idea why this should happen. It makes no sense at all.
Perhaps someone else will have an idea.

Edit: I can't stop thinking about this. There's something very wrong here. It defeats the whole purpose of being able to write functions to re-use code. I'm having enormous trouble believing that it's really happening. There won't be any 'residual' in registers to affect values the next time the function is called.
To be honest, I'd need to see it to believe that it's really happening.

Hi,

The reason you have your AtoD values interacting may be because you only have ONE ADC, it is switched from input to input.
The ADC has a resistor in series with its input and a small capacitor connected to the input and gnd.
When you change from A0 to A1 to A3 etc, it takes time for the ADC capacitor to acquire the voltage of the next input because it still has the charge from the previous channel read.
The solution is for you to read each input twice and use the last reading.

Also Putting

int(Thermistor(analogRead(1))

inside a print command is not really good.

You need to read your analogue inputs once and store them in a variable, then use that variable throughout your sketch, reading them at the start of each loop.

int PoolThermistor = A0;
int HeatInThermistor = A1;
int HeatOutThermistor = A2;
int PoolTempRAW;
int HeatInletTempRAW;
int HeatOutletTempRAW;



void setup() {
  pinMode(PoolThermistor, INPUT);
  pinMode(HeatInThermistor, INPUT);
  pinMode(HeatOutThermistor, INPUT);

}

void loop() {
PoolTempRAW = analogRead(PoolThermistor);
PoolTempRAW = analogRead(PoolThermistor);
HeatInletTempRAW = analogRead(HeatInThermistor);
HeatInletTempRAW = analogRead(HeatInThermistor);
HeatOutletTempRAW = analogRead(HeatOutThermistor);
HeatOutletTempRAW = analogRead(HeatOutThermistor);

}

Just a suggested method of getting you analog readings.

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

Hope it helps... Tom... :slight_smile:

TomGeorge:
Hi,

The reason you have your AtoD values interacting may be because you only have ONE ADC, it is switched from input to input.
The ADC has a resistor in series with its input and a small capacitor connected to the input and gnd.
When you change from A0 to A1 to A3 etc, it takes time for the ADC capacitor to acquire the voltage of the next input because it still has the charge from the previous channel read.
The solution is for you to read each input twice and use the last reading.

That doesn't account for why it happens only when a single function is used for all of the reads, but not when the function is copied so that there's a separate copy for each sensor. It should have absolutely nothing to do with the variables used in the actual function that calls the analogue read. In both cases, the ADC channel is switched in the same way.

I do agree though, that best results are obtained by doing multiple reads when switching channels.

Hi,

If the original OP post is looked at loop, that is exaclty what he is doing, reading each analog input one after the other.
That by the way apart from setup is the only part of the sketch that is being used.
The functions aren't used in the loop().
Try this if you like.

#include <LiquidCrystal.h>


LiquidCrystal lcd (2, 3, 4, 5, 6, 7); /* 2=rs; 3=enable; 4=D4; 5=D5; 6=D6; 7=D7 */

int PoolThermistor = A0;
int HeatInThermistor = A1;
int HeatOutThermistor = A2;
int PoolTempRAW;
int HeatInletTempRAW;
int HeatOutletTempRAW;



void setup() {
  lcd.begin(20, 4);
  pinMode(PoolThermistor, INPUT);
  pinMode(HeatInThermistor, INPUT);
  pinMode(HeatOutThermistor, INPUT);

}

void loop() {
  //Gather the raw thermistor data
PoolTempRAW = analogRead(PoolThermistor);
PoolTempRAW= analogRead(PoolThermistor);
HeatInletTempRAW= analogRead(HeatInThermistor);
HeatInletTempRAW= analogRead(HeatInThermistor);
HeatOutletTempRAW= analogRead(HeatOutThermistor);
HeatOutletTempRAW= analogRead(HeatOutThermistor);
  //LCD print the raw data
lcd.setCursor(0,0);
 lcd.print("Pool:");
 lcd.setCursor(12,0); 
 lcd.print(int(PoolTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,1);
 lcd.print("Heater in:");
 lcd.setCursor(12,1); 
 lcd.print(int(HeatInletTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,2);
 lcd.print("Heater out:");
 lcd.setCursor(12,2); 
 lcd.print(int(HeatOutletTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 
 delay(1500);

 lcd.clear();
}

It compiles, but not tried.
Tom... :slight_smile:
Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

TomGeorge:
Hi,

If the original OP post is looked at loop, that is exaclty what he is doing, reading each analog input one after the other.

Yes, and he says that code works fine, using three separate functions for the calculations. The only difference between that and the version I posted is that one function is used for the calculations after the analogue read instead of the original three. His working code doesn't do multiple reads of each sensor to get the correct result.

TomGeorge:
The functions aren't used in the loop().

I somehow missed this line in your last post. You didn't read his post well enough.
Of course they're used in 'loop()':-
(That was the whole point of the discussion and my earliest replies.)

lcd.print(int(Thermistor1(analogRead(0))));
.
lcd.print(int(Thermistor2(analogRead(1))));
.
lcd.print(int(Thermistor3(analogRead(2))));
.
lcd.print(int(Thermistor4(analogRead(3))));

(I didn't put the above in code tags because then the 'bold' formatting wouldn't have worked.)

Hi,
Point taken...Just unusual to put so much in a serial.print.
Considering the temperature values will be used in other parts of the sketch.

Tom.... :slight_smile:

TomGeorge:
Hi,
Point taken...Just unusual to put so much in a serial.print.
Considering the temperature values will be used in other parts of the sketch.

Tom.... :slight_smile:

I agree. And all those brackets.....

It's still a complete mystery what caused the problem when just one function was used instead of four individual functions doing exactly the same thing.
I'll have nightmares about it tonight. :frowning:

Thank you for looking into this.

@TomGeorge -- I didn't know about the RC internal to the chip. that makes sense, considering that is how I would use a digital pin for analog reads -- the time constant idea -- I'll give your corrections a go, I'll bet it works a lot better that way. I was trying to find a way to separate the pins and reads from the loop, but in my lack of knowledge and experience with this...

@OldSteve -- I didn't mean to give you nightmares about this. I agree, all the brackets to me is not a good thing. I definitely would like a function that works on it's own, without needing to be set up in multiple instances. To me that is taking up needless 1/4 cycles with all the fetches etc. Those do add up.

My ignorance in coding becomes very obvious. But even given that, I recognized that this is sloppy coding, hence the posting here.

I'll get a schematic posted today, so you know what you are looking at.

Ok, Here's the schematic:

I have compiled Tom's code, and loaded it on the mini.

Yes, that does the trick for separating the response of each unit, though that does leave me with the challenge of converting each of the raw readings. I suspect that the readings I'm getting are actually millivolts, and not degrees kelvin. Example 450K does not equal 65F. Sitting in my work area I'm getting readings centered around that 450, varying by only 3% over time.

The function as written in the playground postings does a nice job of converting to *F. Now to figure a way to use that function on each of the raw readings, without having to have it in code 4 times, as posted in my OP.

I suspect that I'll need to write a loop that will do each in turn, then place the results in another set of registers. Let me give that a try, and post what does and doesn't work.

Hi,

Use your function you have made to do the conversion.
Just call it four times, once after each analogRead.
You will have to do 4 conversions.
The reading you get are the ADC output which is 0 to 1023, which represents 0 to 5Vdc

Your schematic has very little resolution, cannot read it.
Does your CAD have an image export facility?

Tom... :slight_smile:

That was a link to the image on Photobucket, Tom. Here it is in the flesh:-

Not sure how to attach the .sch file to a post. The sch is done in eagle, so if you know of a way, I'm willing to learn how to do it.

For the conversion function, the reference pages have this:

int ReadSens_and_Condition(){
  int i;
  int sval = 0;

  for (i = 0; i < 5; i++){
    sval = sval + analogRead(0);    // sensor on analog pin 0
  }

  sval = sval / 5;    // average
  sval = sval / 4;    // scale to 8 bits (0 - 255)
  sval = 255 - sval;  // invert output
  return sval;
}

I can see how to have it read each sensor twice, and average the results RawADC, but am having trouble figuring out how to make it do this for each thermistor without having to write it 4 times.

Could this be a part of the solution?

//conversion to *F
int temp = double Thermistor(int RawADC);

Add this after each set of readings, like:

void loop() {
//Gather the raw thermistor data
PoolTempRAW = analogRead(PoolThermistor);
PoolTempRAW= analogRead(PoolThermistor);
int temp;

HeatInletTempRAW= analogRead(HeatInThermistor);
HeatInletTempRAW= analogRead(HeatInThermistor);
int temp;

HeatOutletTempRAW= analogRead(HeatOutThermistor);
HeatOutletTempRAW= analogRead(HeatOutThermistor);
int temp;

PumpTempRAW= analogRead(PumpThermistor);
PumpTempRAW= analogRead(PumpThermistor);
int temp;
[\code]

In Eagle:
File:Export:Image, saves as a .png that you can Attach to a message.

No joy so far....

#include <math.h>
#include <LiquidCrystal.h>


LiquidCrystal lcd (2, 3, 4, 5, 6, 7); /* 2=rs; 3=enable; 4=D4; 5=D5; 6=D6; 7=D7 */

// thermistor pins
int PoolThermistor = A0;
int HeatInThermistor = A1;
int HeatOutThermistor = A2;
int PumpThermistor = A3;

// raw output registers
int PoolTempRAW;
int HeatInletTempRAW;
int HeatOutletTempRAW;
int PumpTempRAW;

//converted readings
int PoolTempConF;
int HeatInConF;
int HeatOutConF;
int PumpConF;

//conversion function
double Thermistor(int RawADC) {
 double Temp;
 Temp = log(10000.0*((1024.0/RawADC-1))); 
//         =log(10000.0/(1024.0/RawADC-1)) // for pull-up configuration
 Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
 Temp = Temp - 273.15;            // Convert Kelvin to Celcius
 Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return (temp);
}
//conversion call
//int temp = convert();

void setup() {
  lcd.begin(20, 4);
  pinMode(PoolThermistor, INPUT);
  pinMode(HeatInThermistor, INPUT);
  pinMode(HeatOutThermistor, INPUT);
  pinMode(PumpThermistor, INPUT);

}

void loop() {
//Gather the raw thermistor data
PoolTempRAW = analogRead(PoolThermistor);
PoolTempRAW= analogRead(PoolThermistor);
int temp;

HeatInletTempRAW= analogRead(HeatInThermistor);
HeatInletTempRAW= analogRead(HeatInThermistor);
int temp;

HeatOutletTempRAW= analogRead(HeatOutThermistor);
HeatOutletTempRAW= analogRead(HeatOutThermistor);
int temp;

PumpTempRAW= analogRead(PumpThermistor);
PumpTempRAW= analogRead(PumpThermistor);
int temp;



//LCD print the raw data
 lcd.setCursor(0,0);
 lcd.print("Pool:");
 lcd.setCursor(12,0); 
 lcd.print(int(PoolTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,1);
 lcd.print("Heater in:");
 lcd.setCursor(12,1); 
 lcd.print(int(HeatInletTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 lcd.setCursor(0,2);
 lcd.print("Heater out:");
 lcd.setCursor(12,2); 
 lcd.print(int(HeatOutletTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");
 
 lcd.setCursor(0,3);
 lcd.print("Pump:");
 lcd.setCursor(12,3); 
 lcd.print(int(PumpTempRAW));
 lcd.print(" ");
 lcd.print((char)223);
 lcd.print("F ");

 
 delay(1500);
 


 lcd.clear();
}

Will not compile.

Error:

Arduino: 1.6.8 (Windows 7), Board: "Arduino Pro or Pro Mini, ATmega328 (5V, 16 MHz)"

--snip-- (removed the c:program files lines......)

C:\Users\bob\Desktop\arduino downloads\pool_thermometer2\pool_thermometer2.ino: In function 'double Thermistor(int)':

pool_thermometer2:34: error: 'temp' was not declared in this scope

  return (temp);

          ^

C:\Users\bob\Desktop\arduino downloads\pool_thermometer2\pool_thermometer2.ino: In function 'void loop()':

pool_thermometer2:56: error: redeclaration of 'int temp'

 int temp;

     ^

pool_thermometer2:52: error: 'int temp' previously declared here

 int temp;

     ^

pool_thermometer2:60: error: redeclaration of 'int temp'

 int temp;

     ^

pool_thermometer2:52: error: 'int temp' previously declared here

 int temp;

     ^

pool_thermometer2:64: error: redeclaration of 'int temp'

 int temp;

     ^

pool_thermometer2:52: error: 'int temp' previously declared here

 int temp;

     ^

Multiple libraries were found for "LiquidCrystal.h"
 Used: C:\Users\bob\Desktop\arduino downloads\libraries\LiquidCrystal
 Not used: C:\Program Files (x86)\Arduino\libraries\LiquidCrystal
Using library LiquidCrystal at version 1.0.4 in folder: C:\Users\bob\Desktop\arduino downloads\libraries\LiquidCrystal 
exit status 1
'temp' was not declared in this scope

Thanks Crossroads!

I was thinking they meant the actual .sch file.