Spa Controller project - beginner needing tutorial link

Hi All

I am starting a project to make a more advanced spa pool controller than the one supplied (timer on/off, thermostat knob) which is horrible inefficient.

I have most of it planned out, the basic premise is

  • Time I want it at a comfortable temperature (times we generally use the spa)
  • Time it can drop to a maintenance temperature (times it is unused i.e overnight)
  • be able to turn on/off the spa and lighting by a push button

I also intend to control the lighting around the spa pool and create a delay when you turn off the spa that the lights stay on for 5 minutes while you leave. This all involves RTC, relays, analogue sensors, 16x2 LCD c/w 5 buttons, all pretty basic stuff.

Where I am slightly stuck is using the 16x2 LCD with 5 integrated buttons. I have a lot of information about creating menus and displaying values which I have got going fine but I want to set the temperature and store that value to be used by the code i.e adjust the comfort level from 38 deg to 40 deg. I am struggling to find a succinct tutorial about modifying and applying values via buttons and LCD. I can find lots of code on this forum regarding changing values but the code is always mixed up with their particular project making it confusing and it is often far more complex than what i need.

Does anyone know of a simple tutorial/project to create something like a fan or led that turns on/off at a temperature that is adjustable by a keypad and LCD screen i.e if temp > [value set by keypad] turn on LED. If you can give me a link that would be fantastic I can learn from there. Only reason I'm not trawling through google myself is I'm heading away for a week and away from internet but want to "play" while I'm away and have run out of time trying to find something so I thought the generous community may help

Cheers
Al

Don't become a slave to the hardware you picked for the user interface.
'Push button menu interfaces' are popular on commercial products because they are cheap, not because they are good, which frequently they are not.

For example, I have a commercial irrigation controller that requires a dozen button presses to manually switch on a single valve for testing purposes and it drives me nuts. They could of added a button for each of the 6 valves, but that would of increased the manufacturing cost by a dollar, so they opted for a rubbish user interface instead.

The whole point of a DIY project is that you get to choose how you want things to be and that includes the user interface.
For example, if you need to set a temperature and a delay there is nothing more intuitive than having two rotary controls, (potentiometer or a rotary encoders).
If you need on/off buttons, then use real push button switches just like you would find on a piece of machinery. They aren't that expensive when you consider the cost of running a spa and the hours you will spend on this project.

If you need to add a dozen physical controls to make the system user friendly, then just do it. There is plenty of Arduino hardware that can cope with lots of inputs.

If you have obscure settings that hardly ever change then I would make them editable via MQTT commands. There are user friendly Windows/Android apps that make these kind of changes a breeze. No need to mess around with menus.

Use the display to tell you what the software is doing, for example, 'pump off, lights going off in 2 minutes' etc.
You should never find yourself in a situation where you have to ask the question 'what's it doing now!' - that is the ultimate indicator of failure.

This is NOT a beginner's project.

Thanks mikb55

I realise I am using “popular “ hardware, I don’t intend to use that particular bit in the final design as it is not water proof and I don’t really need menus for this particular project (I do for others) I do want an LCD display and a method of changing the desired temp though. We have guests and ultimately I want them to be able to adjust the temp within preset limits of 36-42 deg.

Basically I’m looking for a simple code or tutorial on how to set an adjustable temp via a lcd display and up and down buttons. Or more broadly how to change an interger in the code by user input, this could be via potentiometer or up/down.

I take your comments on board though, Keep it simple however I would still like to learn how to do what I’m asking even if I don’t go down that road at the end knowledge is knowledge. I like the idea of control via web or app but maybe not this project

Cheers
Al

Thanks jremington

I have made a few projects before, all basic stuff controlled by a few sensors such as automatic doors, fan controls, pump controls etc. But in comparison to some of the coding that goes on here this is baby steps. I can display data on LDC's quite happily but have never done a project with user adjustable variables.

I guess there is no easy learning curve from the comments so far other than to pull apart someone else's project and reverse engineer the code I'm looking for. I was hoping there was a nice basic code example of a user input to adjust an integer which is displayed via an LCD.

Cheers
Al

With a small display and 5 button system, basically you have two mode, navigate and edit. As you have so few buttons, their behaviour must change in accordance with the mode.
In navigation mode you use buttons to move through a list of variables, or a tree structure that contains lists of variables.
In edit mode you increment/decrement via buttons, validate automatically in code, save or abandon via buttons.

The problem with a small display is that as the project grows you spend more and more time lost in the tree of settings because there are no visual landmarks to tell you where you have come from or where you are going.

What you want to do sounds fairly simple. I assume you have something like the LCD keypad shield. If so, here's a tutorial: https://create.arduino.cc/projecthub/electropeak/using-1602-lcd-keypad-shield-w-arduino-w-examples-e02d95?f=1

It sounds like you have no need for menus, it's basically a loop that reads the buttons and if the up button was pressed, raise the desired temp and the reverse for the down button.

There will be more than that - displaying current temp for instance, but to begin with make something that just proves you can read the buttons and display something. Perhaps display the temp on serial to start.

Something you've not asked about but I am going to tell you anyway in the hope that it helps.

There are 2 ways to control a heater: with hysteresis and with PWM in some form.

Hysteresis implies that the required temperature is never achieved, or at least, not for long. Mostly the temperature is above or below what you want, even if only by half a degree or so.

If you use PWM you match the heat going in to the heat being lost and thus maintain equilibrium at the required temperature. For a hobby project you can spend as much time as you like fine tuning this to work really well. I'm guessing you have electric heaters, so in this case PWM means burst firing of a triac to supply the power, for example 10 cycles on and 10 cycles off would be half power. You reduce the on number of cycles and increase the off number of cycles as you get closer to the desired temperature. You also need an offset (slight increase) to the desired temperature because you don't want to reduce the power to zero at the desired temperature, there needs to be some power input to make up for heat lost to the environment.

I rather suspect that the amount of water in a spa is such that simple bang bang control with a bit of hysteresis will be sufficient.

"There are 2 ways to control a heater: with hysteresis and with PWM in some form."

Relays and mechanical thermostats are usually used for heating large masses whose temperature does not change quickly. Relays can operate much faster than the temperature change of a spa pool.

Wildbill and Zoomkat, you both miss the point. Hysteresis implies the temperature goes up and down by whatever the hysteresis interval is or more. PWM means it doesn't have to. You can do PWM for heating with relays. My heating system uses PWM with a 720 second cycle time and relay to control the motor valves. Works wonderfully.

Thank you all for your replies over night.

Thanks Wildbill for the tutorial link - I'm heading off today and will take that with me.

Thanks Zoomkat and PerryBebbington for the ideas and concepts around Hysteresis and PWM.

I may combine these ideas. The initial project is just do have the ability to set comfort temperature and the spa to maintain this during a hard coded comfort period, I'll start with a simple bang bang relay.

Using PWM to maintain the exact temp has merit, because it is a basic restive element operating a temp a lot higher than what I need applying energy in cycles is going to save power but still maintain the heat. Although it is 1.5m3 of water so I'm not sure how fast that will cool so I'll need to do the math, is it better to use PWM to keep the element at 40 deg constantly or use all 1100w for 1min every 10 min to maintain a temp within 1 deg of 40 deg.

During the economy period where the temp can drop I might use the hysteresis model allowing a slightly wider range of temp so for example, if temp < 25 then on if temp > 27 then off, this allows 2 degrees of cooling. Reason being that the blower motor has to run when the heater is on and I don't want that to cycle more than once per hr, even better would be every 2-3 hrs but it will depend on ambient temp.

it may end up something like;
Economy Period - 2 deg variance (cycle on every 3 hrs for example)
Heating to comfort - full on until temp reached
Maintain comfort - 1 deg variance (cycle on every 30 min so it is ready for use)
Spa in use - PWM (cycle to maintain desired temp with heat loss as cover is off)

I intend to add some form of basic data logging to see how fast the spa cools and how long it takes to recover the heat. I also need to know how long it takes to raise it between economy and comfort and will probably add in a second temp sensor just for the air temp to get an idea.

I take on board the comments by MIKB55 about the small screen and tiny menus. Future version may have some form of web interface to set times etc, I like that idea but a bit beyond me currently, but winter is coming, time to stay inside and learn away.

Anyway thanks for your help and ideas it is early days in the project and I'm sure I'll have more specific questions as I get into it.

Cheers
Al

I've spent years perfecting my heating controller, adding improvements as I have gone along. Start simple, if it works the way you want, fine. If you think it could be better then add improvements until you are happy with it. It does not have to be perfect from the start.

PerryBebbington:
Start simple, if it works the way you want, fine. If you think it could be better then add improvements until you are happy with it. It does not have to be perfect from the start.

This is a very important point. Projects can easily get derailed by trying to do it all at the beginning. Of course you want to know where (you think) you're going but it's so much easier for many reasons to just "Eat that elephant one bite at a time".

Thanks for the advice, I do intend to start simple, I'll finish hard coding everything to start with using a few relays an on/off button as an override and an LM35 sensor just to prove that it works, currently halfway through (started on desk then will move to spa), then add in the LCD and user adjustment for temp, followed by data logging in version 3, then may get a bit fancy with the LCD screen to set current time and on/off times, web page or app maybe version 10.

Just setting a variable via an LCD and two buttons for up/down will keep me occupied to start with, nice holiday project while I'm away, baby steps.

Also just to add for interest sake

The project concept came from an underfloor heating controller we use from Devi, it has the comfort and economy period plus energy readouts on a small touch screen. I could almost use one of these off the shelf and connect it to the spa, but where is the fun in that :slight_smile:

Cheers
Al

Allan_Pritchard:
I want to set the temperature and store that value to be used by the code i.e adjust the comfort level from 38 deg to 40 deg. I am struggling to find a succinct tutorial about modifying and applying values via buttons and LCD.

Allan_Pritchard:
Basically I’m looking for a simple code or tutorial on how to set an adjustable temp via a lcd display and up and down buttons. Or more broadly how to change an interger in the code by user input, this could be via potentiometer or up/down.

Assuming it is one of these shields as linked in #6, have a look at member jurs' method in #8 here.

(Not a tutorial as such, alas.)

It shows the default value on the screen. Then you arrow left and right to the digit you want to change, then arrow up and down to set that digit, then select to commit. #22 there says you'll need to atoi the string to get a usable number.

Hi,

I have managed to figure my way through making a simple up/down to set the desired spa temp. using the code from post 8 as mentioned above. I have changed some of the code to use standard coding terms out of the reference, even though the way it was done was probably more efficient.

Although this is more of a programming question than a Project Guidance I though I would post here.

So I have a 1602 shield with the 5 buttons. I have made a simple up/down set between 36 and 42. I thought I would be clever and add a second line with the current temperature. his is run of a simple LM35 connected to A5 pin.

My issue is if I run the current temperature part of the code it works fine and displays the current temp (currently 21 deg here) and updates every minute on line one of the LCD, if I put my finger on it it will increase to 30 deg

if I run the code for the adjustable desired temperature it displays correctly on line 2 and can be adjusted up or down as desired,

As soon as I have both running in the same loop the top line immediately reads 41 and putting my finger on it it goes to 57. Somehow it seems the comftemp and curTemp seem to be mixed up.

I have been tackling this all afternoon and can't work it out. Hopefully someone can see my error.

PS this is not how the final project will look, simply just trying to get these two components working.

Code is below

#include <LiquidCrystal.h> // Library for 1602 LCD

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // set the pins for the dispay, if using shield these are standard

#define btnNONE 0
#define btnSELECT 1
#define btnLEFT 2
#define btnUP 3
#define btnDOWN 4
#define btnRIGHT 5
#define NUM_KEYS 6

const byte ButtonsPin = A0;
int comfTemp = 40; // default spa temp
boolean lcdNeedsUpdate = true;

int tempPin = A5;   // the output pin of LM35
int curTemp; // defines current spa temp
long lastread; // to allow reading of spa temp once per minute

int read_LCD_buttons() // returns a value from 0-6 using the define list matching the button to corrosponding value
{
  int returnValue;
  // read ADC value of pressed button
  int adc_key_in =  analogRead (ButtonsPin); // reads the value from A0 to determin button
  int adc_key_in1 =  analogRead (ButtonsPin); // reads the value again, used to check the value is constant
  // read again and check for stable ADC reading (software debouncing for analog input)
  if (abs(adc_key_in1 - adc_key_in) > 1) return btnNONE; // if ADC reading is not stable, return btnNONE
  // below values for the buttons coneected to A0 may vary depending on the sheild used, these need to be worked out prior
  if (adc_key_in < 50) returnValue = btnRIGHT;
  else if ( (adc_key_in > 95) && (adc_key_in < 110) ) returnValue = btnUP;
  else if ( (adc_key_in > 250) && (adc_key_in < 350) ) returnValue = btnDOWN;
  else  if ( (adc_key_in > 400) && (adc_key_in < 450) ) returnValue = btnLEFT;
  else if ( (adc_key_in > 630) && (adc_key_in < 700) ) returnValue = btnSELECT;
  else returnValue = btnNONE;
  // simple "blocking" code: "Busy waiting" until button is released by user
  while (adc_key_in < 999) adc_key_in = analogRead(ButtonsPin);
  return returnValue;
}

int readTemp() // get the temperature and convert it to celsius
{
  curTemp = analogRead(tempPin);
  return curTemp * 0.48828125;
}

void setup()
{
  Serial.begin(9600); // begin serial monitor so you can view the output of selected temp
  lcd.begin(16, 2); // initialise the display
  lcd.clear(); // clear the display
  lcd.print ("Current Temp:"); // will display current temp of spa
  lcd.setCursor (0, 1);
  lcd.print ("Comfort Temp:"); // will display desired temp

  pinMode(tempPin, INPUT); // defines temp pin as input from LM35
}


void loop()
{
  if ((millis() - 60000) >= lastread) { // check if last temp reading was greater than 1 min
    curTemp = readTemp();     // get the temperature
    lcd.setCursor(14, 0); // adds the current temp to the end of the first line
    lcd.print(curTemp); // should print onto line one at end
    lastread = millis(); // set the lastread of sensor
  }


  int key = read_LCD_buttons();
  if (key != btnNONE) lcdNeedsUpdate = true;

  switch (key)
  {
    case btnUP:
      if (comfTemp < 42) comfTemp++;
      break;
    case btnDOWN:
      if (comfTemp > 36) comfTemp--;
      break;
    case btnSELECT:
      Serial.print("Saved value: ");
      Serial.println(comfTemp);
      Serial.println(curTemp);
      break;
  }

  if (lcdNeedsUpdate)
  {
    lcd.setCursor(14, 1); // should print onto line 2 
    lcd.print(comfTemp);
    lcdNeedsUpdate = false;
  }

}

Hi I changed my milis code to

if (millis() >= lastread + 10000) { // check if last temp reading was greater than 10 sec

When I run the full code the current temp is blank for the first 10 secs then approximately 19 to 21 then after the next 10 sec is 40. I can't work out why it fails but if I only the code below the temp is correct, approx 22 deg. It has to be something in the subsequent code but I can't see it. Full updated code is at the bottom.

{
  if (millis() >= lastread + 10000) { // check if last temp reading was greater than 10 sec
    curTemp = readTemp();     // get the temperature
    lcd.setCursor(14, 0); // adds the current temp to the end of the first line
    lcd.print(curTemp); // should print onto line one at end
    lastread = millis(); // set the lastread of sensor
  }
}
#include <LiquidCrystal.h> // Library for 1602 LCD

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // set the pins for the dispay, if using shield these are standard

#define btnNONE 0
#define btnSELECT 1
#define btnLEFT 2
#define btnUP 3
#define btnDOWN 4
#define btnRIGHT 5
#define NUM_KEYS 6

const byte ButtonsPin = A0;
int comfTemp = 40; // default spa temp
boolean lcdNeedsUpdate = true;

int tempPin = A5;   // the output pin of LM35
int curTemp = 0; // defines current spa temp
long lastread = 0; // to allow reading of spa temp once per minute

int read_LCD_buttons() // returns a value from 0-6 using the define list matching the button to corrosponding value
{
  int returnValue;
  // read ADC value of pressed button
  int adc_key_in =  analogRead (ButtonsPin); // reads the value from A0 to determin button
  int adc_key_in1 =  analogRead (ButtonsPin); // reads the value again, used to check the value is constant
  // read again and check for stable ADC reading (software debouncing for analog input)
  if (abs(adc_key_in1 - adc_key_in) > 1) return btnNONE; // if ADC reading is not stable, return btnNONE
  // below values for the buttons coneected to A0 may vary depending on the sheild used, these need to be worked out prior
  if (adc_key_in < 50) returnValue = btnRIGHT;
  else if ( (adc_key_in > 95) && (adc_key_in < 110) ) returnValue = btnUP;
  else if ( (adc_key_in > 250) && (adc_key_in < 350) ) returnValue = btnDOWN;
  else  if ( (adc_key_in > 400) && (adc_key_in < 450) ) returnValue = btnLEFT;
  else if ( (adc_key_in > 630) && (adc_key_in < 700) ) returnValue = btnSELECT;
  else returnValue = btnNONE;
  // simple "blocking" code: "Busy waiting" until button is released by user
  while (adc_key_in < 999) adc_key_in = analogRead(ButtonsPin);
  return returnValue;
}

int readTemp() // get the temperature and convert it to celsius
{
  curTemp = analogRead(tempPin);
  return curTemp * 0.48828125;
}

void setup()
{
  Serial.begin(9600); // begin serial monitor so you can view the output of selected temp
  lcd.begin(16, 2); // initialise the display
  lcd.clear(); // clear the display
  lcd.print ("Current Temp:"); // will display current temp of spa
  lcd.setCursor (0, 1);
  lcd.print ("Comfort Temp:"); // will display desired temp

  pinMode(tempPin, INPUT); // defines temp pin as input from LM35
}


void loop()
{
  if (millis() >= lastread + 10000) { // check if last temp reading was greater than 10 sec
    curTemp = readTemp();     // get the temperature
    lcd.setCursor(14, 0); // adds the current temp to the end of the first line
    lcd.print(curTemp); // should print onto line one at end
    lastread = millis(); // set the lastread of sensor
  }


  int key = read_LCD_buttons();
  if (key != btnNONE) lcdNeedsUpdate = true;

  switch (key)
  {
    case btnUP:
      if (comfTemp < 42) comfTemp++;
      break;
    case btnDOWN:
      if (comfTemp > 36) comfTemp--;
      break;
    case btnSELECT:
      Serial.print("Saved value: ");
      Serial.println(comfTemp);
      Serial.println(curTemp);
      break;
  }

  if (lcdNeedsUpdate)
  {
    lcd.setCursor(14, 1); // should print onto line 2 
    lcd.print(comfTemp);
    lcdNeedsUpdate = false;
  }
 
}