Nearly SOLVED Using While function is only giving a partial answer

I see this

DateTime theTime = dst_rtc.calculateTime(standardTime);  // takes into account DST

but you go on to use the now DateTime object in your calculations and logic.

Either adjust now or use theTime instead.

DST should be applied when you read the time, nothing else should need to know or care.

a7

Thank you @alto777, that has corrected my errors of not giving enough/any thought when combining parts of other sketches into this project.

Evening, moving onto the next stage of this project, which is becoming more of a challenge as with my limited knowledge, I have created a problem in that although the controls now work as planned and I managed to get the LCD backlight to turn off after a short priod of time, I would like to introduce four buttons to eventually be able to change some of the operating parameters with changing them via the laptop. For this I am using the Ezbutton library. Thde problem I am having is with line 265 as KI am not sure what to change the line to work with the EzButton library. As always your thoughts or suggestions would be most appreciated.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 28/04/24
 Description : A project to display the current date, time and temperature on the 20x4 LCD screen
 and create a thermostatic heating control of three zones to include frost protection.
 This has been based on the RTClib ds3231 example along with many parts borrowed
 from several other files/sketches and in collaboration with several authors from the
 Arduino Forum in particular alto777, blh64, StefanL38 and UKHeliBob. */
#include "RTClib.h"
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <DST_RTC.h>

#define ONE_WIRE_BUS_1 3
#define ONE_WIRE_BUS_2 4
#define ONE_WIRE_BUS_3 5

#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
// Only required for Serial on Zero based boards
#define Serial SERIAL_PORT_USBVIRTUAL
#endif

OneWire oneWire_range(ONE_WIRE_BUS_1);
OneWire oneWire_club(ONE_WIRE_BUS_2);
OneWire oneWire_airgun(ONE_WIRE_BUS_3);

const byte range_relay = 10;  // Relay outputs
const byte club_relay = 11;
const byte airgun_relay = 12;

float range_temperature;
float club_temperature;
float airgun_temperature;

const float range_temperature_setpoint = 24.0;   // C
const float club_temperature_setpoint = 24.0;    // C
const float airgun_temperature_setpoint = 24.0;  // C
const float frost_setpoint = 24.0;               // C
const float deadzone = 1.0;                      // C

ezButton leftbutton(6);   // create ezButton object that attach to pin 6;
ezButton upbutton(7);     // create ezButton object that attach to pin 7;
ezButton downbutton(8);   // create ezButton object that attach to pin 8;
ezButton rightbutton(9);  // create ezButton object that attach to pin 9;

const unsigned printInterval = 3000;
const unsigned ScreenOnTime = 30000;

int buttonState = 0;
int lastButtonState = LOW;  // the previous reading from the input pin
int backLightState;

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisScreen = 0;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

bool screenOn = false;

DallasTemperature range_sensor(&oneWire_range);
DallasTemperature club_sensor(&oneWire_club);
DallasTemperature airgun_sensor(&oneWire_airgun);

enum { SUNDAY,
       MONDAY,
       TUESDAY,
       WEDNESDAY,
       THURSDAY,
       FRIDAY,
       SATURDAY };

const int On_Normal = 16 * 60UL + 00;   // 16:00
const int Off_Normal = 21 * 60UL + 30;  // 21:30
const int On_Sunday = 12 * 60UL + 00;   // 12:00 for Sundays
const int Off_Sunday = 17 * 60UL + 30;  // 17:30 for Sundays

LiquidCrystal_I2C lcd(0X27, 20, 4);
RTC_DS3231 rtc;
DST_RTC dst_rtc;  // DST object

// Define US or EU rules for DST comment out as required. More countries could be added with different rules in DST_RTC.cpp
// const char rulesDST[] = "US"; // US DST rules
const char rulesDST[] = "EU";  // EU DST rules

char daysOfTheWeek[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
byte char_temp[8] = { B00100, B01010, B01010, B01110, B01110, B11111, B11111, B01110 };  // for thermometer icon

void setup() {

  pinMode(range_relay, OUTPUT);
  pinMode(club_relay, OUTPUT);
  pinMode(airgun_relay, OUTPUT);

  leftbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  upbutton.setDebounceTime(50);     // set debounce time to 50 milliseconds
  downbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  rightbutton.setDebounceTime(50);  // set debounce time to 50 milliseconds

  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);

  Wire.begin();
  rtc.begin();

  lcd.init();
  lcd.backlight();
  lcd.createChar(0, char_temp);
  lcd.setCursor(4, 0);
  lcd.print("Take it easy!");
  lcd.setCursor(4, 1);
  lcd.print("Working on it");
  lcd.setCursor(2, 2);
  lcd.print("G0RJM Three Zone");
  lcd.setCursor(1, 3);
  lcd.print("Temperature Control");
  delay(1000);
  lcd.clear();
  if (rtc.lostPower()) {
    Serial.println("RTC is NOT running!");
    // Uncomment the following line to set the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // rtc.adjust(DateTime(2024,4,24,21,49,00));
    /* DST? If we're in it, let's subtract an hour from the RTC time to keep our DST calculation correct. This gives us
    Standard Time which our DST check will add an hour back to if we're in DST.
    */
    DateTime standardTime = rtc.now();
    if (dst_rtc.checkDST(standardTime) == true) {  // check whether we're in DST right now. If we are, subtract an hour.
      standardTime = standardTime.unixtime() - 3600;
    }
    rtc.adjust(standardTime);
  }

  Serial.println("G0RJM Three zone Temperature Control");

  range_sensor.begin();
  club_sensor.begin();
  airgun_sensor.begin();

  pinMode(LED_BUILTIN, OUTPUT);

}  // End of setup

void loop() {


  //----------------------------Button control-----------------------//

  leftbutton.loop();   // MUST call the loop() function first
  upbutton.loop();     // MUST call the loop() function first
  downbutton.loop();   // MUST call the loop() function first
  rightbutton.loop();  // MUST call the loop() function first

  //----------------------------Left---------------------------------//

  if (leftbutton.isPressed()) {
    digitalWrite(range_relay, HIGH);
    Serial.println("The left button is pressed");
  }
  if (leftbutton.isReleased()) {
    digitalWrite(range_relay, LOW);
    Serial.println("The left button is released");
  }

  //-----------------------------Up----------------------------------//

  if (upbutton.isPressed()) {
    digitalWrite(club_relay, HIGH);
    Serial.println("Up button is pressed");
  }

  if (upbutton.isReleased()) {
    digitalWrite(club_relay, LOW);
    Serial.println("Up button is released");
  }

  //------------------------------Down--------------------------------//

  if (downbutton.isPressed()) {
    digitalWrite(airgun_relay, HIGH);
    Serial.println("Down button is pressed");
  }

  if (downbutton.isReleased()) {
    digitalWrite(airgun_relay, LOW);
    Serial.println("Down button is released");
  }

  //-------------------------------Right-----------------------------//

  if (rightbutton.isPressed()) {
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.println("Right button is pressed");
  }
  if (rightbutton.isReleased()) {
    digitalWrite(LED_BUILTIN, LOW);
    Serial.println("Right button is released");
  }

  //------------------------------Print delay---------------------------//

  unsigned long currentMillis = millis();                 // all time elements are data type unsigned long
  if (currentMillis - previousMillis >= printInterval) {  // enough time passed yet?
    previousMillis = previousMillis + printInterval;      // yes, so set time for the next print event
                                                          // capture & print data, or print latest data, etc.
                                                          // closing curly brace at end of loop.

    //-----------------------Daylight Saving Setting----------------------//

    DateTime standardTime = rtc.now();

    Serial.println("Standard Time");
    printTheTime(standardTime);

    DateTime theTime = dst_rtc.calculateTime(standardTime);  // takes into account DST

    Serial.println("Time adjusted for Daylight Saving Time");
    printTheTime(theTime);

    //delay(1000);  // 1 second

    //--------------------------Timeswitch Control------------------------//

    int currentTime = theTime.hour() * 60UL + theTime.minute();

    //---------------------Timed heating setting------------------------//

    bool isHeatOn;  // true = heating time

    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to saturday so use normal times
      if (currentTime < On_Normal || currentTime > Off_Normal) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    }

    if (isHeatOn) {
      Serial.println("Normal Heating ");
      NormalTemperature();
    } else {
      Serial.println("Frost Prevention  ");
      FrostPrevention();
    }

    // ----------------------Backlight display--------------------------//


    buttonState = digitalRead(rightbutton);
    int reading = digitalRead(rightbutton);

    if (reading != lastButtonState) {
      // reset the debouncing timer
      lastDebounceTime = millis();
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {
      // whatever the reading is at, it's been there for longer than the debounce
      // delay, so take it as the actual current state:

      // if the button state has changed:
      if (reading != buttonState) {
        buttonState = reading;
        // only toggle the backlight if the new button state is HIGH

        if (buttonState == HIGH) {
          backLightState = !backLightState;
        }
      }
    }

    if (buttonState == HIGH) {
      previousMillisScreen = millis();
      lcd.backlight();
      screenOn = true;

      lastButtonState = reading;
    }


    // if time passed above SCREEN_ON_TIME after we turned it on
    if ((buttonState == LOW) && (millis() - previousMillisScreen >= ScreenOnTime)) {
      lcd.noBacklight();
      screenOn = false;
      Serial.println("Backlight OFF");
    }

    //-------------------------Date display-----------------------------//

    lcd.setCursor(0, 0);
    lcd.print(daysOfTheWeek[theTime.dayOfTheWeek()]);
    lcd.setCursor(3, 0);
    lcd.print(":");
    lcd.setCursor(4, 0);
    if (theTime.day() < 10) lcd.print('0');
    lcd.print(theTime.day(), DEC);
    lcd.print(':');
    if (theTime.month() < 10) lcd.print('0');
    lcd.print(theTime.month(), DEC);
    lcd.print(':');
    lcd.print(theTime.year(), DEC);

    //-----------------------Time display-------------------------------//

    lcd.setCursor(15, 0);
    if (theTime.hour() < 10) lcd.print('0');
    lcd.print(theTime.hour(), DEC);
    lcd.print(':');
    if (theTime.minute() < 10) lcd.print('0');
    lcd.print(theTime.minute(), DEC);

    //---------------------Temperature display - -----------------------//

    lcd.setCursor(0, 1);  // Range
    lcd.print("Range: ");
    lcd.print(range_sensor.getTempCByIndex(0));

    lcd.setCursor(12, 1);
    lcd.write((char)223);
    lcd.print('C');

    lcd.setCursor(0, 2);  // Clubroom
    lcd.print("Club:  ");
    lcd.print(club_sensor.getTempCByIndex(0));

    lcd.setCursor(12, 2);
    lcd.write((char)223);
    lcd.print('C');

    lcd.setCursor(0, 3);  // Airgun Range
    lcd.print("Airgun:");
    lcd.print(airgun_sensor.getTempCByIndex(0));

    lcd.setCursor(12, 3);
    lcd.write((char)223);
    lcd.print('C');
   
    //-------------------------Serial print-----------------------------//

    Serial.print("Requesting temperatures...");
    range_sensor.requestTemperatures();
    club_sensor.requestTemperatures();
    airgun_sensor.requestTemperatures();
    Serial.println(" done");
    Serial.print("Range: ");
    Serial.println(range_sensor.getTempCByIndex(0));
    Serial.print("Club: ");
    Serial.println(club_sensor.getTempCByIndex(0));
    Serial.print("Airgun: ");
    Serial.println(airgun_sensor.getTempCByIndex(0));
  }

}  // End of loop

//-------------------------Button debounce------------------------------//
/*
void getDebounce() {

  int reading = digitalRead(rightbutton);  // Change to represent correct name of button.

  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;
      // only toggle the backlight if the new button state is HIGH

      if (buttonState == HIGH) {
        backLightState = !backLightState;  // Change to correct description of button state.
      }
    }
  }

  if (buttonState == HIGH) {
    previousMillisScreen = millis();
    lcd.backlight();
    screenOn = true;

    lastButtonState = reading;
  }
}
*/
//-----------------------Print time to serial---------------------------//
void printTheTime(DateTime theTimeP) {

  Serial.print(theTimeP.year(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.month(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.day(), DEC);
  Serial.print(' ');
  Serial.print(theTimeP.hour(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.minute(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.second(), DEC);
  Serial.println();
  lcd.setCursor(15, 0);
}

//---------------------Frost Prevention control-------------------------//
void FrostPrevention() {

  if ((range_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  F");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF F");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  F");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF F");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  F");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF F");
    }
  }
}

//------------------------Normal Temperature control--------------------//
void NormalTemperature() {

  if ((range_sensor.getTempCByIndex(0) <= (range_temperature_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  T");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (range_temperature_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF T");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (club_temperature_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  T");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (club_temperature_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF T");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (airgun_temperature_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  T");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (airgun_temperature_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF T");
    }
  }
}

The error report is here.


FQBN: arduino:avr:uno
Using board 'uno' from platform in folder: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6
Using core 'arduino' from platform in folder: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6

Detecting libraries used...
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for RTClib.h: [RTClib@2.1.4]
ResolveLibrary(RTClib.h)
  -> candidates: [RTClib@2.1.4]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for Adafruit_I2CDevice.h: [Adafruit BusIO@1.16.0]
ResolveLibrary(Adafruit_I2CDevice.h)
  -> candidates: [Adafruit BusIO@1.16.0]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for Wire.h: [Wire@1.0]
ResolveLibrary(Wire.h)
  -> candidates: [Wire@1.0]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for OneWire.h: [OneWire@2.3.7]
ResolveLibrary(OneWire.h)
  -> candidates: [OneWire@2.3.7]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for LiquidCrystal_I2C.h: [LiquidCrystal I2C@1.1.2]
ResolveLibrary(LiquidCrystal_I2C.h)
  -> candidates: [LiquidCrystal I2C@1.1.2]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for ezButton.h: [ezButton@1.0.4]
ResolveLibrary(ezButton.h)
  -> candidates: [ezButton@1.0.4]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C -Ic:\Users\johnm\Documents\Arduino\libraries\ezButton\src C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for DallasTemperature.h: [MAX31850 DallasTemp@1.1.6 DallasTemperature@3.9.0]
ResolveLibrary(DallasTemperature.h)
  -> candidates: [MAX31850 DallasTemp@1.1.6 DallasTemperature@3.9.0]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C -Ic:\Users\johnm\Documents\Arduino\libraries\ezButton\src -Ic:\Users\johnm\Documents\Arduino\libraries\DallasTemperature C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for EEPROM.h: [EEPROM@2.0]
ResolveLibrary(EEPROM.h)
  -> candidates: [EEPROM@2.0]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C -Ic:\Users\johnm\Documents\Arduino\libraries\ezButton\src -Ic:\Users\johnm\Documents\Arduino\libraries\DallasTemperature -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\EEPROM\src C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Alternatives for DST_RTC.h: [DST RTC@1.1.1]
ResolveLibrary(DST_RTC.h)
  -> candidates: [DST RTC@1.1.1]
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C -Ic:\Users\johnm\Documents\Arduino\libraries\ezButton\src -Ic:\Users\johnm\Documents\Arduino\libraries\DallasTemperature -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\EEPROM\src -Ic:\Users\johnm\Documents\Arduino\libraries\DST_RTC C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o nul
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_DS1307.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_DS3231.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_Micros.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_Millis.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_PCF8523.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTC_PCF8563.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\RTClib\src\RTClib.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO\Adafruit_BusIO_Register.cpp
Alternatives for SPI.h: [SPI@1.0]
ResolveLibrary(SPI.h)
  -> candidates: [SPI@1.0]
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO\Adafruit_I2CDevice.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO\Adafruit_SPIDevice.cpp
Using cached library dependencies for file: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src\Wire.cpp
Using cached library dependencies for file: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src\utility\twi.c
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\OneWire\OneWire.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C\LiquidCrystal_I2C.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\ezButton\src\ezButton.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\DallasTemperature\DallasTemperature.cpp
Using cached library dependencies for file: c:\Users\johnm\Documents\Arduino\libraries\DST_RTC\DST_RTC.cpp
Using cached library dependencies for file: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\SPI\src\SPI.cpp
Generating function prototypes...
C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\variants\standard -Ic:\Users\johnm\Documents\Arduino\libraries\RTClib\src -Ic:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire\src -Ic:\Users\johnm\Documents\Arduino\libraries\OneWire -Ic:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C -Ic:\Users\johnm\Documents\Arduino\libraries\ezButton\src -Ic:\Users\johnm\Documents\Arduino\libraries\DallasTemperature -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\EEPROM\src -Ic:\Users\johnm\Documents\Arduino\libraries\DST_RTC -IC:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\SPI\src C:\Users\johnm\AppData\Local\Temp\arduino\sketches\607E4C43C662DFE7EC2ADFF35B324932\sketch\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp -o C:\Users\johnm\AppData\Local\Temp\192394947\sketch_merged.cpp
C:\Users\johnm\AppData\Local\Arduino15\packages\builtin\tools\ctags\5.8-arduino11/ctags -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives C:\Users\johnm\AppData\Local\Temp\192394947\sketch_merged.cpp
Compiling sketch...
"C:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.6\\cores\\arduino" "-IC:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.6\\variants\\standard" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\RTClib\\src" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\Adafruit_BusIO" "-IC:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.6\\libraries\\Wire\\src" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\OneWire" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\LiquidCrystal_I2C" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\ezButton\\src" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\DallasTemperature" "-IC:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.6\\libraries\\EEPROM\\src" "-Ic:\\Users\\johnm\\Documents\\Arduino\\libraries\\DST_RTC" "-IC:\\Users\\johnm\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.6\\libraries\\SPI\\src" "C:\\Users\\johnm\\AppData\\Local\\Temp\\arduino\\sketches\\607E4C43C662DFE7EC2ADFF35B324932\\sketch\\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp" -o "C:\\Users\\johnm\\AppData\\Local\\Temp\\arduino\\sketches\\607E4C43C662DFE7EC2ADFF35B324932\\sketch\\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino.cpp.o"
C:\Users\johnm\Documents\Arduino\Three_zone_heating_controller_V1.2.8_Next_DST.4\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino: In function 'void loop()':
C:\Users\johnm\Documents\Arduino\Three_zone_heating_controller_V1.2.8_Next_DST.4\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino:265:42: error: cannot convert 'ezButton' to 'uint8_t {aka unsigned char}' for argument '1' to 'int digitalRead(uint8_t)'
     buttonState = digitalRead(rightbutton);
                                          ^
C:\Users\johnm\Documents\Arduino\Three_zone_heating_controller_V1.2.8_Next_DST.4\Three_zone_heating_controller_V1.2.8_Next_DST.4.ino:266:42: error: cannot convert 'ezButton' to 'uint8_t {aka unsigned char}' for argument '1' to 'int digitalRead(uint8_t)'
     int reading = digitalRead(rightbutton);
                                          ^
Multiple libraries were found for "DallasTemperature.h"
  Used: C:\Users\johnm\Documents\Arduino\libraries\DallasTemperature
  Not used: C:\Users\johnm\Documents\Arduino\libraries\MAX31850_DallasTemp
Using library RTClib at version 2.1.4 in folder: C:\Users\johnm\Documents\Arduino\libraries\RTClib 
Using library Adafruit BusIO at version 1.16.0 in folder: C:\Users\johnm\Documents\Arduino\libraries\Adafruit_BusIO 
Using library Wire at version 1.0 in folder: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\Wire 
Using library OneWire at version 2.3.7 in folder: C:\Users\johnm\Documents\Arduino\libraries\OneWire 
Using library LiquidCrystal I2C at version 1.1.2 in folder: C:\Users\johnm\Documents\Arduino\libraries\LiquidCrystal_I2C 
Using library ezButton at version 1.0.4 in folder: C:\Users\johnm\Documents\Arduino\libraries\ezButton 
Using library DallasTemperature at version 3.9.0 in folder: C:\Users\johnm\Documents\Arduino\libraries\DallasTemperature 
Using library EEPROM at version 2.0 in folder: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\EEPROM 
Using library DST RTC at version 1.1.1 in folder: C:\Users\johnm\Documents\Arduino\libraries\DST_RTC 
Using library SPI at version 1.0 in folder: C:\Users\johnm\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.6\libraries\SPI 
exit status 1

Compilation error: cannot convert 'ezButton' to 'uint8_t {aka unsigned char}' for argument '1' to 'int digitalRead(uint8_t)'

You need to learn about ezButton.

edit: You need to read your own code! After a closer fly over to see if there were other issues I note that you have everything about the ezButton already going on.

So never mind.

The ez-est way is to study an example, I will attach one here, but right away

    buttonState = digitalRead(rightbutton);
    int reading = digitalRead(rightbutton);

You do not read an ezButton in that manner. The error was trying to send an ezButton into a function expecting an integer.

sketch.ino:265:42: error: cannot convert 'ezButton' to 'uint8_t {aka unsigned char}' for argument '1' to 'int digitalRead(uint8_t)'
buttonState = digitalRead(rightbutton);

ezButtons make it easy:

/* ezButton ezButton.h */
// make an ezButton seen on pin 7
ezButton button(7);


// add a line for each button to your *setup*()
  button.setDebounceTime(20);


// call this every time you loop your loop for each ezButton object
  button.loop();


// simple recognizing that 'button' became pressed
  if (button.isPressed()) {
    Serial.println("The button got pressed");

and your buttons should be wired between the input pin and ground.

Make this work, it really is all you need to know, then you'd know:

button/examples/04.SingleButtonAll/04.SingleButtonAll.ino at master · ArduinoGetStarted/button · GitHub

HTH
This text will be hidden

a7

OK the code can be changed for the rightbutton ezButton object.

    buttonState = digitalRead(rightbutton);
    int reading = digitalRead(rightbutton);

should be

    buttonState = rightbutton.getState();
    int reading = buttonState;

but the entire section of code there is pointless if you have an ezButton. All you need to do is react to what all those loop() calls on your button objects observed and can report.

So you press the button, something happens. All the old state/new state/debouncing is... no your problem any more.

a7

Thank you @alto777 for the great suggestion which I have carefully re-read and implimented.
As suggested I have removed the "old debounce" from the sketch which has simplified things.
I can now move onto the next part which is using the buttons to present a menu on the LCD and then ultimately change some of the preset parameters. Well that is the plan at present.

Afternoon, I am back for a little more guidance please. The project has moved on a little and I have the four buttons in place and proven to be working when pressed. I am using the EZButton library to take care of the 'debounce'.
I have managed to get the 'left button' to work via a switch case and this displays th four options on the LCD.
However, whatever I have tried to get the 'up button' or 'down button' to make and incremental increase or decrease of the selected temperature setpoint, I seem to have failed. Many hours later, as I was hoping that I could work out a solution, I am asking for some gudiance please.
The up and down buttons are shown inbetween lines 219-232.
The action part of the sketch is between line 402 and 422. Once I have conquered the code for one menu option, I can hopefully duplicate it for the other two options.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 23/08/2024
 Description : A project to display the current date, time and temperature on the 20x4 LCD screen
 and create a thermostatic heating control of three zones to include frost protection.
 This has been based on the RTClib ds3231 example along with many parts borrowed
 from several other files/sketches and in collaboration with several authors from the
 Arduino Forum in particular alto777, blh64, StefanL38 and UKHeliBob. */
#include "RTClib.h"
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <DST_RTC.h>

#define ONE_WIRE_BUS_1 3
#define ONE_WIRE_BUS_2 4
#define ONE_WIRE_BUS_3 5
#define TEMPERATURE_PRECISION 11  // 0.125deg resolution

OneWire oneWire_range(ONE_WIRE_BUS_1);
OneWire oneWire_club(ONE_WIRE_BUS_2);
OneWire oneWire_airgun(ONE_WIRE_BUS_3);

const byte range_relay = 10;  // Relay outputs
const byte club_relay = 11;
const byte airgun_relay = 12;

float range_temperature;
float club_temperature;
float airgun_temperature;

float range_temperature_setpoint = 22.5;   // C
float club_temperature_setpoint = 23.0;    // C
float airgun_temperature_setpoint = 23.5;  // C
const float frost_setpoint = 12.0;         // C
const float deadzone = 1.0;                // C

float CounterR = 1;
float CounterC = 1;
float CounterA = 1;
float new_Setpoint1;

ezButton leftbutton(6);   // create ezButton object that is attached to pin 6;
ezButton upbutton(7);     // create ezButton object that is attached to pin 7;
ezButton downbutton(8);   // create ezButton object that is attached to pin 8;
ezButton rightbutton(9);  // create ezButton object that is attached to pin 9;

const int ledPin = 13;

const unsigned printInterval = 3000;
const unsigned backlightOnTime = 15000;
const unsigned menuOnTime = 30000;
const unsigned upTime = 10000;
const unsigned downTime = 10000;

int selector = 0;
int upadjust = 0;
int downadjust = 0;
int buttonState = 0;
int lastButtonState = LOW;  // the previous reading from the input pin

unsigned long startMillis = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisMenu = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisAdjustUp = 0;
unsigned long previousMillisAdjustDown = 0;
unsigned long backlightOffTime = 0;
unsigned long menuOffTime = 0;

bool backlightOn = false;
bool menuOn = false;
bool timeOut = false;
bool adjustUp = false;
bool adjustDown = false;
bool rangeAdjust = false;
bool clubAdjust = false;
bool airgunAdjust = false;

DallasTemperature range_sensor(&oneWire_range);
DallasTemperature club_sensor(&oneWire_club);
DallasTemperature airgun_sensor(&oneWire_airgun);

enum { SUNDAY,
       MONDAY,
       TUESDAY,
       WEDNESDAY,
       THURSDAY,
       FRIDAY,
       SATURDAY };

const int On_Normal = 16 * 60UL + 00;   // 16:00
const int Off_Normal = 21 * 60UL + 30;  // 21:30
const int On_Sunday = 12 * 60UL + 00;   // 12:00 for Sundays
const int Off_Sunday = 17 * 60UL + 30;  // 17:30 for Sundays

LiquidCrystal_I2C lcd(0X27, 20, 4);
RTC_DS3231 rtc;
DST_RTC dst_rtc;  // DST object

// Define US or EU rules for DST comment out as required. More countries could be added with different rules in DST_RTC.cpp
// const char rulesDST[] = "US"; // US DST rules
const char rulesDST[] = "EU";  // EU DST rules

char daysOfTheWeek[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
byte char_temp[8] = { B00100, B01010, B01010, B01110, B01110, B11111, B11111, B01110 };  // for thermometer icon

void setup() {

  pinMode(range_relay, OUTPUT);
  pinMode(club_relay, OUTPUT);
  pinMode(airgun_relay, OUTPUT);
  pinMode(ledPin, OUTPUT);

  leftbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  upbutton.setDebounceTime(50);     // set debounce time to 50 milliseconds
  downbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  rightbutton.setDebounceTime(50);  // set debounce time to 50 milliseconds

  Serial.begin(9600);

  startMillis = millis();

  Wire.begin();
  rtc.begin();

  lcd.init();
  lcd.backlight();
  lcd.createChar(0, char_temp);
  lcd.setCursor(2, 0);
  lcd.print("G0RJM Three Zone");
  lcd.setCursor(1, 1);
  lcd.print("Temperature Control");
  lcd.setCursor(4, 2);
  lcd.print("Take it easy!");
  lcd.setCursor(4, 3);
  lcd.print("Working on it");

  delay(1000);
  lcd.clear();
  if (rtc.lostPower()) {
    Serial.println("RTC is NOT running!");
    // Uncomment the following line to set the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // rtc.adjust(DateTime(2024,4,24,21,49,00));
    /* DST? If we're in it, let's subtract an hour from the RTC time to keep our DST calculation correct. This gives us
    Standard Time which our DST check will add an hour back to if we're in DST.
    */
    DateTime standardTime = rtc.now();
    if (dst_rtc.checkDST(standardTime) == true) {  // check whether we're in DST right now. If we are, subtract an hour.
      standardTime = standardTime.unixtime() - 3600;
    }
    rtc.adjust(standardTime);
  }

  Serial.println("G0RJM Three zone Temperature Control");

  range_sensor.begin();
  club_sensor.begin();
  airgun_sensor.begin();

  range_sensor.setResolution(TEMPERATURE_PRECISION);
  club_sensor.setResolution(TEMPERATURE_PRECISION);
  airgun_sensor.setResolution(TEMPERATURE_PRECISION);

}  // End of setup

void loop() {

  //       ("ABCDEFGHIJ1234567890")

  //----------------------------Button control-----------------------//

  leftbutton.loop();   // MUST call the loop() function first
  upbutton.loop();     // MUST call the loop() function first
  downbutton.loop();   // MUST call the loop() function first
  rightbutton.loop();  // MUST call the loop() function first

  // --------------------------Backlight display--------------------------//

  unsigned long currentMillis = millis();  // all time elements are data type unsigned long

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed())) {
    TemperatureDisplay();
  }

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed() || (leftbutton.isPressed() || (upbutton.isPressed() || (downbutton.isPressed()))))) {

    lcd.backlight();
    backlightOffTime = millis() + 10000;
  }

  if (millis() >= backlightOffTime) {
    lcd.noBacklight();
    TemperatureDisplay();
  }

  //--------------------------------Menu----------------------------------//

  if (leftbutton.isPressed()) {
    menuOn = true;
    SettingMenu();
    selector++;  // this is done after SettingMenu(), so case 0 will be executed on the first button press
    if (selector > 3) {
      selector = 0;
    }
    previousMillisMenu = currentMillis;
  }

  if (menuOn) {
    if (currentMillis - previousMillisMenu >= menuOnTime) {
      menuOn = false;
    }
  }

  if (upbutton.isPressed()) {
    adjustUp = true;
    previousMillisAdjustUp = currentMillis;
  }
  if (adjustUp) {
    if (currentMillis - previousMillisAdjustUp >= upTime) {
      adjustUp = false;
    }
  }

  if (downbutton.isPressed()) {
    adjustDown = true;
    previousMillisAdjustDown = currentMillis;
  }

  if (adjustDown) {
    if (currentMillis - previousMillisAdjustDown >= downTime) {
      adjustDown = false;
    }
  }
  //------------------------------Print delay---------------------------//

  if (currentMillis - previousMillis >= printInterval)  // enough time passed yet?
  {
    previousMillis = previousMillis + printInterval;  // yes, so set time for the next print event
                                                      // capture & print data, or print latest data, etc.
                                                      // closing curly brace at end of loop.

    //-----------------------Daylight Saving Setting----------------------//

    DateTime standardTime = rtc.now();

    Serial.println("Standard Time");
    printTheTime(standardTime);

    DateTime theTime = dst_rtc.calculateTime(standardTime);  // takes into account DST

    Serial.println("Time adjusted for Daylight Saving Time");
    printTheTime(theTime);


    //--------------------------Timeswitch Control------------------------//

    int currentTime = theTime.hour() * 60UL + theTime.minute();

    //---------------------Timed heating setting------------------------//

    bool isHeatOn;  // true = heating time

    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to saturday so use normal times
      if (currentTime < On_Normal || currentTime > Off_Normal) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    }
    if (!menuOn && !adjustUp && !adjustDown) {

      if (isHeatOn) {
        Serial.println("Normal Heating ");
        NormalTemperature();
      } else {
        Serial.println("Frost Prevention  ");
        FrostPrevention();
      }
    }

    //-----------------------Date & Time display-------------------------//

    if (!menuOn && !adjustUp && !adjustDown) {

      lcd.setCursor(0, 0);
      lcd.print(daysOfTheWeek[theTime.dayOfTheWeek()]);
      lcd.setCursor(3, 0);
      lcd.print(":");
      lcd.setCursor(4, 0);
      if (theTime.day() < 10) lcd.print('0');
      lcd.print(theTime.day(), DEC);
      lcd.print(':');
      if (theTime.month() < 10) lcd.print('0');
      lcd.print(theTime.month(), DEC);
      lcd.print(':');
      lcd.print(theTime.year(), DEC);
      lcd.setCursor(15, 0);
      if (theTime.hour() < 10) lcd.print('0');
      lcd.print(theTime.hour(), DEC);
      lcd.print(':');
      if (theTime.minute() < 10) lcd.print('0');
      lcd.print(theTime.minute(), DEC);
    }

    //-------------------------Serial print-----------------------------//

    Serial.print("Requesting temperatures...");
    range_sensor.requestTemperatures();
    club_sensor.requestTemperatures();
    airgun_sensor.requestTemperatures();
    Serial.println(" done");
    Serial.print("Range: ");
    Serial.println(range_sensor.getTempCByIndex(0));
    Serial.print("Club: ");
    Serial.println(club_sensor.getTempCByIndex(0));
    Serial.print("Airgun: ");
    Serial.println(airgun_sensor.getTempCByIndex(0));
  }

}  // End of loop

//-----------------------Print time to serial---------------------------//
void printTheTime(DateTime theTimeP) {

  Serial.print(theTimeP.year(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.month(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.day(), DEC);
  Serial.print(' ');
  Serial.print(theTimeP.hour(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.minute(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.second(), DEC);
  Serial.println();
}

//-----------------------Setting Menu-------------------------------//
void SettingMenu() {

  switch (selector) {
    case 0:
      Serial.println("Case 0");
      MenuScreen();
      break;
    case 1:
      Serial.println("Case 1");
      MenuScreenRange();
      break;
    case 2:
      Serial.println("Case 2");
      MenuScreenClub();
      break;
    case 3:
      Serial.println("Case 3");
      MenuScreenAirgun();
  }
}
//------------------------Menu screen-------------------------------//
void MenuScreen() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint);
  lcd.setCursor(11, 1);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint);
  lcd.setCursor(11, 2);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint);
  lcd.setCursor(11, 3);
  lcd.write((char)223);
  lcd.print('C');
}

void MenuScreenRange() {

  rangeDisplay();

  if (adjustUp) {
    rangeAdjust = true;
    airgunAdjust = false;
    clubAdjust = false;
    range_temperature_setpoint++;
    Serial.println("Upwards");
    rangeDisplay();
  }

  if (adjustDown) {
    airgunAdjust = false;
    clubAdjust = false;
    Serial.println("Downwards");
    range_temperature_setpoint--;
    rangeDisplay();
  }
}

void MenuScreenClub() {

  clubDisplay();
  if (adjustUp) {
    clubAdjust = true;
    rangeAdjust = false;
    airgunAdjust = false;
    club_temperature_setpoint++;
    clubDisplay();
  }
  
  if (adjustDown) {
    rangeAdjust = false;
    airgunAdjust = false;
    club_temperature_setpoint--;
    clubDisplay();
  }
}

void MenuScreenAirgun() {

  airgunDisplay();
  if (adjustUp) {
    airgunAdjust = true;
    rangeAdjust = false;
    clubAdjust = false;
    airgun_temperature_setpoint++;
    airgunDisplay();
  }

  if (adjustDown) {
    rangeAdjust = false;
    clubAdjust = false;
    airgun_temperature_setpoint--;
    airgunDisplay();
  }
}
//-------------------------Update Display---------------------------//
void rangeDisplay() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint);
  lcd.setCursor(11, 1);
  lcd.write((char)223);
  lcd.print('C');
}

void clubDisplay() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint);
  lcd.setCursor(11, 2);
  lcd.write((char)223);
  lcd.print('C');
}

void airgunDisplay() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint);
  lcd.setCursor(11, 3);
  lcd.write((char)223);
  lcd.print('C');
}

//---------------------Temperature display - -----------------------//
void TemperatureDisplay() {

  lcd.setCursor(0, 1);  // Range
  lcd.print("Range: ");
  lcd.print(range_sensor.getTempCByIndex(0));

  lcd.setCursor(11, 1);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 2);  // Clubroom
  lcd.print("Club:  ");
  lcd.print(club_sensor.getTempCByIndex(0));

  lcd.setCursor(11, 2);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 3);  // Airgun Range
  lcd.print("Airgun:");
  lcd.print(airgun_sensor.getTempCByIndex(0));

  lcd.setCursor(11, 3);
  lcd.write((char)223);
  lcd.print('C');
}

//---------------------Frost Prevention control-------------------------//
void FrostPrevention() {

  if ((range_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  F");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF F");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  F");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF F");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  F");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF F");
    }
  }
}

//------------------------Normal Temperature control--------------------//
void NormalTemperature() {

  if ((range_sensor.getTempCByIndex(0) <= (range_temperature_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  T");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (range_temperature_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF T");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (club_temperature_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  T");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (club_temperature_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF T");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (airgun_temperature_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  T");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (airgun_temperature_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF T");
    }
  }
}

Hope that my description makes some sense.

not enough sense.

the mimimum you should post is a detailed description of

  • what are the buttons uses for?

  • the wanted behaviour

If I press button ExactNameOfThisButton then .....

but instead I see

  • detailed description of the observed behaviour.

I press button "exactNameOf ThisButton"
then happends.......

the sequence in which your different functions were called

You are using a lot of bolean flag-variables and have to check them in many places.

I suggest that you learn how a code-concept named state-machine works

A state-machine minimises the number of if-conditions to a minimum.
You already use the most important language of state-machines
which is the
switch-case-break-statement

depending on what function shall be active only a part of the code is executed.
Always only that part of the code that is nescessary to execute in this situation.

The break; ensures that all other cases are skipped which means all skipped cases are not executed.

For more analysing the code-behaviour you can add serial printing to your code

here are four so called macros that give more comfort for the debug-printing

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

dbg prints a fixed text the variable-NAME and the variable-value each time the line is executed

dbgi prints only once every interval-seconds. You define the interval. For example only print once every two seconds

dbgc only prints once if the value of the variable has changed

dbgc is for integers

dbgcf is for floats

Example

void loop() {
  dbgc("01",menuOn);
  dbgc("02",adjustUp);
  dbgc("03",adjustDown);
  dbgc("04",selector);

print variable once if value of variable has changed

The actual adjustment is done in MenuScreenRange().

void MenuScreenRange() {

  rangeDisplay();

  if (adjustUp) {
    rangeAdjust = true;
    airgunAdjust = false;
    clubAdjust = false;
    range_temperature_setpoint++;
    Serial.println("Upwards");
    rangeDisplay();
  }

  if (adjustDown) {
    airgunAdjust = false;
    clubAdjust = false;
    Serial.println("Downwards");
    range_temperature_setpoint--;
    rangeDisplay();
  }
}

It's not clear to me that MenuScreenRange() is actually called in the string of calls after the button press. You will need to walk through your program step by step to see what is called when.

Thanks @cattledog, I have carefully gone through the script and added some more detail descriptions along with some serial prints to assist me with troubleshooting. After a lot of trial and error, I have got the sketch to work.

Thanks @StefanL38 for the very comprehensive code for debugging. I have not used it yet, as I need to re-read it a couple more times to start to understand how best to impliment it. It will be usefull in the future.

Here is a tutorial about it

Just to bore you even more, this is the latest working version of the sketch.
I am sure that there could be ways to reduce/simplify the coding but as it works, I am happy to leave well alone.
The next phase of this project is to save the changes to the three setpoint values to eeprom and then call them as required. I could well be coming back for further advice after I have given it a try.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 01/09/2024
 Description : A project to display the current date, time and temperature on the 20x4 LCD screen
 and create a thermostatic heating control of three zones to include frost protection.
 This has been based on the RTClib ds3231 example along with many parts borrowed
 from several other files/sketches and in collaboration with several authors from the
 Arduino Forum in particular alto777, blh64, cattledog, StefanL38 and UKHeliBob. */
#include "RTClib.h"
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <DST_RTC.h>

#define ONE_WIRE_BUS_1 3
#define ONE_WIRE_BUS_2 4
#define ONE_WIRE_BUS_3 5
#define TEMPERATURE_PRECISION 11  // 0.125deg resolution

OneWire oneWire_range(ONE_WIRE_BUS_1);
OneWire oneWire_club(ONE_WIRE_BUS_2);
OneWire oneWire_airgun(ONE_WIRE_BUS_3);

const byte range_relay = 10;  // Relay outputs
const byte club_relay = 11;
const byte airgun_relay = 12;

float range_temperature;
float club_temperature;
float airgun_temperature;

float range_temperature_setpoint = 22.5;   // C
float club_temperature_setpoint = 23.0;    // C
float airgun_temperature_setpoint = 23.5;  // C
const float frost_setpoint = 12.0;         // C
const float deadzone = 1.0;                // C

ezButton leftbutton(6);   // create ezButton object that is attached to pin 6;
ezButton upbutton(7);     // create ezButton object that is attached to pin 7;
ezButton downbutton(8);   // create ezButton object that is attached to pin 8;
ezButton rightbutton(9);  // create ezButton object that is attached to pin 9;

const int ledPin = 13;

const unsigned printInterval = 3000;
const unsigned backlightOnTime = 30000;
const unsigned menuOnTime = 30000;
const unsigned upTime = 50;
const unsigned downTime = 50;

int selector = 0;

unsigned long startMillis = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisMenu = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisAdjustUp = 0;
unsigned long previousMillisAdjustDown = 0;
unsigned long backlightOffTime = 0;
unsigned long menuOffTime = 0;

bool backlightOn = false;
bool menuOn = false;        // if = True Initiiates the setup menu
bool adjustUp = false;      // if = True Setpoint increment
bool adjustDown = false;    // if = True Setpoint decrement
bool rangeAdjust = false;   // if = True allows the range setpoint to be adjusted 
bool clubAdjust = false;    // if = True allows the club setpoint to be adjusted 
bool airgunAdjust = false;  // if = True allows the airgun setpoint to be adjusted

DallasTemperature range_sensor(&oneWire_range);
DallasTemperature club_sensor(&oneWire_club);
DallasTemperature airgun_sensor(&oneWire_airgun);

enum { SUNDAY,
       MONDAY,
       TUESDAY,
       WEDNESDAY,
       THURSDAY,
       FRIDAY,
       SATURDAY };

const int On_Normal = 16 * 60UL + 00;   // 16:00
const int Off_Normal = 21 * 60UL + 30;  // 21:30
const int On_Sunday = 12 * 60UL + 00;   // 12:00 for Sundays
const int Off_Sunday = 17 * 60UL + 30;  // 17:30 for Sundays

LiquidCrystal_I2C lcd(0X27, 20, 4);
RTC_DS3231 rtc;
DST_RTC dst_rtc;  // DST object

// Define US or EU rules for DST comment out as required. More countries could be added with different rules in DST_RTC.cpp
// const char rulesDST[] = "US"; // US DST rules
const char rulesDST[] = "EU";  // EU DST rules

char daysOfTheWeek[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
byte char_temp[8] = { B00100, B01010, B01010, B01110, B01110, B11111, B11111, B01110 };  // for thermometer icon

void setup() {

  pinMode(range_relay, OUTPUT);
  pinMode(club_relay, OUTPUT);
  pinMode(airgun_relay, OUTPUT);
  pinMode(ledPin, OUTPUT);

  leftbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  upbutton.setDebounceTime(50);     // set debounce time to 50 milliseconds
  downbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  rightbutton.setDebounceTime(50);  // set debounce time to 50 milliseconds

  Serial.begin(9600);

  startMillis = millis();

  Wire.begin();
  rtc.begin();

  lcd.init();
  lcd.backlight();
  lcd.createChar(0, char_temp);
  lcd.setCursor(2, 0);
  lcd.print("G0RJM Three Zone");
  lcd.setCursor(1, 1);
  lcd.print("Temperature Control");
  lcd.setCursor(4, 2);
  lcd.print("Take it easy!");
  lcd.setCursor(4, 3);
  lcd.print("Working on it");

  delay(1000);
  lcd.clear();
  if (rtc.lostPower()) {
    Serial.println("RTC is NOT running!");
    // Uncomment the following line to set the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // rtc.adjust(DateTime(2024,4,24,21,49,00));
    /* DST? If we're in it, let's subtract an hour from the RTC time to keep our DST calculation correct. This gives us
    Standard Time which our DST check will add an hour back to if we're in DST.
    */
    DateTime standardTime = rtc.now();
    if (dst_rtc.checkDST(standardTime) == true) {  // check whether we're in DST right now. If we are, subtract an hour.
      standardTime = standardTime.unixtime() - 3600;
    }
    rtc.adjust(standardTime);
  }

  Serial.println("G0RJM Three zone Temperature Control");

  range_sensor.begin();
  club_sensor.begin();
  airgun_sensor.begin();

  range_sensor.setResolution(TEMPERATURE_PRECISION);
  club_sensor.setResolution(TEMPERATURE_PRECISION);
  airgun_sensor.setResolution(TEMPERATURE_PRECISION);

}  // End of setup

void loop() {

  //       ("ABCDEFGHIJ1234567890")

  //----------------------------Button control-----------------------//

  leftbutton.loop();   // MUST call the loop() function first
  upbutton.loop();     // MUST call the loop() function first
  downbutton.loop();   // MUST call the loop() function first
  rightbutton.loop();  // MUST call the loop() function first

  // --------------------------Backlight display--------------------------//

  unsigned long currentMillis = millis();  // all time elements are data type unsigned long

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed())) {
    TemperatureDisplay();
  }

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed() || (leftbutton.isPressed() || (upbutton.isPressed() || (downbutton.isPressed()))))) {

    lcd.backlight();
    backlightOffTime = millis() + backlightOnTime;
  }

  if (millis() >= backlightOffTime) {
    lcd.noBacklight();
    TemperatureDisplay();
  }

  //--------------------------------Menu----------------------------------//

  if (leftbutton.isPressed()) {
    menuOn = true;  // Initiiates the setup menu
    SettingMenu();
    selector++;  // this is done after SettingMenu(), so case 0 will be executed on the first button press
    if (selector > 3) {
      selector = 0;
    }
    previousMillisMenu = currentMillis;
  }

  if (menuOn) {
    if (currentMillis - previousMillisMenu >= menuOnTime) {
      menuOn = false;
    }
  }

  //------------------------------------------------------------------------------//
  if (upbutton.isPressed()) {
    adjustUp = true;  // Setpoint Adjust upwards is available
    previousMillisAdjustUp = currentMillis;
  }

  if (adjustUp) {
    if (currentMillis - previousMillisAdjustUp >= upTime) {
      adjustUp = false;
    }
  }

  if (adjustUp && rangeAdjust)  // If True, allows the Range setpoint to be adjusted
  {
    range_temperature_setpoint += 0.1;  // Range Setpoint increment
    Serial.println("range_temperature_setpointR");
    RangeDisplay();
  }
  if (adjustUp && clubAdjust)  // If True, allows the Club setpoint to be adjusted
  {
    club_temperature_setpoint += 0.1;  // Club Setpoint increment
    Serial.println("club_temperature_setpoint");
    ClubDisplay();
  } else {
    if (adjustUp && airgunAdjust)  // If True, allows the Airgun setpoint to be adjusted
    {
      airgun_temperature_setpoint += 0.1;  // Airgun Setpoint increment
      Serial.println("airgun_temperature_setpoint");
      AirgunDisplay();
    }
  }
  //--------------------------------------------------------------------------------//
  if (downbutton.isPressed()) {
    adjustDown = true;  // Setpoint Adjust Downwards is available
    previousMillisAdjustDown = currentMillis;
  }
  if (adjustDown) {
    if (currentMillis - previousMillisAdjustDown >= downTime) {
      adjustDown = false;
    }
  }

  if (adjustDown && rangeAdjust)  // If True, allows the Range setpoint to be adjusted
  {
    range_temperature_setpoint -= 0.1;  // Range Setpoint decrement
    Serial.println("range_temperature_setpoint");
    RangeDisplay();
  }

  if (adjustDown && clubAdjust)  // If True, allows the Club setpoint to be adjusted
  {
    club_temperature_setpoint -= 0.1;  // Club Setpoint decrement
    Serial.println("club_temperature_setpointC");
    ClubDisplay();
  } else {

    if (adjustDown && airgunAdjust)  // If True, allows the Airgun setpoint to be adjusted
    {
      airgun_temperature_setpoint -= 0.1;  // Airgun Setpoint decrement
      Serial.println("airgun_temperature_setpoint");
      AirgunDisplay();
    }
  }
  //------------------------------Print delay---------------------------//

  if (currentMillis - previousMillis >= printInterval)  // enough time passed yet?
  {
    previousMillis = previousMillis + printInterval;  // yes, so set time for the next print event
                                                      // capture & print data, or print latest data, etc.
                                                      // closing curly brace at end of loop.

    //-----------------------Daylight Saving Setting----------------------//

    DateTime standardTime = rtc.now();

    Serial.println("Standard Time");
    printTheTime(standardTime);

    DateTime theTime = dst_rtc.calculateTime(standardTime);  // takes into account DST

    Serial.println("Time adjusted for Daylight Saving Time");
    printTheTime(theTime);


    //--------------------------Timeswitch Control------------------------//

    int currentTime = theTime.hour() * 60UL + theTime.minute();

    //---------------------Timed heating setting------------------------//

    bool isHeatOn;  // true = heating time

    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to saturday so use normal times
      if (currentTime < On_Normal || currentTime > Off_Normal) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // outside normal heating time
        isHeatOn = false;
      } else {
        // within normal heating time
        isHeatOn = true;
      }
    }
    if (!menuOn && !adjustUp && !adjustDown) {

      if (isHeatOn) {
        Serial.println("Normal Heating ");
        NormalTemperature();
      } else {
        Serial.println("Frost Prevention  ");
        FrostPrevention();
      }
    }

    //-----------------------Date & Time display-------------------------//

    if (!menuOn && !adjustUp && !adjustDown) {

      lcd.setCursor(0, 0);
      lcd.print(daysOfTheWeek[theTime.dayOfTheWeek()]);
      lcd.setCursor(3, 0);
      lcd.print(":");
      lcd.setCursor(4, 0);
      if (theTime.day() < 10) lcd.print('0');
      lcd.print(theTime.day(), DEC);
      lcd.print(':');
      if (theTime.month() < 10) lcd.print('0');
      lcd.print(theTime.month(), DEC);
      lcd.print(':');
      lcd.print(theTime.year(), DEC);
      lcd.setCursor(15, 0);
      if (theTime.hour() < 10) lcd.print('0');
      lcd.print(theTime.hour(), DEC);
      lcd.print(':');
      if (theTime.minute() < 10) lcd.print('0');
      lcd.print(theTime.minute(), DEC);
    }

    //-------------------------Serial print-----------------------------//

    Serial.print("Requesting temperatures...");
    range_sensor.requestTemperatures();
    club_sensor.requestTemperatures();
    airgun_sensor.requestTemperatures();
    Serial.println(" done");
    Serial.print("Range: ");
    Serial.println(range_sensor.getTempCByIndex(0));
    Serial.print("Club: ");
    Serial.println(club_sensor.getTempCByIndex(0));
    Serial.print("Airgun: ");
    Serial.println(airgun_sensor.getTempCByIndex(0));
  }

}  // End of loop

//-----------------------Print time to serial---------------------------//
void printTheTime(DateTime theTimeP) {

  Serial.print(theTimeP.year(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.month(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.day(), DEC);
  Serial.print(' ');
  Serial.print(theTimeP.hour(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.minute(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.second(), DEC);
  Serial.println();
}

//-----------------------Setting Menu-------------------------------//
void SettingMenu() {

  switch (selector) {
    case 0:
      Serial.println("Case 0");
      MenuScreen();
      break;
    case 1:
      Serial.println("Case 1");
      RangeDisplay();
      break;
    case 2:
      Serial.println("Case 2");
      ClubDisplay();
      break;
    case 3:
      Serial.println("Case 3");
      AirgunDisplay();
  }
}
//------------------------Menu screen-------------------------------//
void MenuScreen() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

//-------------------------Update Display---------------------------//
void RangeDisplay() {

  rangeAdjust = true;
  clubAdjust = false;
  airgunAdjust = false;
  Serial.println("RangeChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

void ClubDisplay() {

  clubAdjust = true;
  rangeAdjust = false;
  airgunAdjust = false;
  Serial.println("ClubChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

void AirgunDisplay() {

  airgunAdjust = true;
  rangeAdjust = false;
  clubAdjust = false;
  Serial.println("AirgunChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

//---------------------Temperature display - -----------------------//
void TemperatureDisplay() {

  lcd.setCursor(0, 1);  // Range
  lcd.print("Range: ");
  lcd.print(range_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 2);  // Club
  lcd.print("Club:  ");
  lcd.print(club_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 3);  // Airgun
  lcd.print("Airgun:");
  lcd.print(airgun_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');
}

//---------------------Frost Prevention control-------------------------//
void FrostPrevention() {

  if ((range_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  F");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF F");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  F");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF F");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  F");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF F");
    }
  }
}

//------------------------Normal Temperature control--------------------//
void NormalTemperature() {

  if ((range_sensor.getTempCByIndex(0) <= (range_temperature_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.print("Range Heating ON  ");
    lcd.setCursor(15, 1);
    lcd.print("ON  T");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (range_temperature_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      Serial.print("Range Heating OFF  ");
      lcd.setCursor(15, 1);
      lcd.print("OFF T");
    }
  }

  if ((club_sensor.getTempCByIndex(0) <= (club_temperature_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.print("Club Heating ON  ");
    lcd.setCursor(15, 2);
    lcd.print("ON  T");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (club_temperature_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      Serial.print("Club Heating OFF  ");
      lcd.setCursor(15, 2);
      lcd.print("OFF T");
    }
  }

  if ((airgun_sensor.getTempCByIndex(0) <= (airgun_temperature_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.print("Airgun Heating ON  ");
    lcd.setCursor(15, 3);
    lcd.print("ON  T");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (airgun_temperature_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      Serial.print("Airgun Heating OFF  ");
      lcd.setCursor(15, 3);
      lcd.print("OFF T");
    }
  }
}

Thanks for all your help. It is most appreciated.

Thank you for the extra guidance. A little more reading for me.

Who, us? :expressionless:

I just scrolled through the thread, the sieve which is my brain having let the details leave, and I have to caution anyone that Capitalization Counts, and here we have

except… it's the ezButton library.

#include <ezButton.h>

They are quite different in approach and implementation. I have not read or used EZButton enough to have an opinion.

a7

Evening @alto777,

Thank you for pointing out my earlier typo errors.
Hopefully the sketch shows the correct library being used.
I had not thought to correct my earlier typos.
I will investigate these a bit further.

Regards John

As this is sent from my mobile, predictive text may have changed what I meant to say.

I will read the code closely later but a quick look shows the right library included and used.

a7

Morning, this project has moved along quite well and is now working more or less as planned. There is one small concern as foillows. When the LCD backlight is OFF and the temperature changes at the sensor, the new temperature value is displayed correctly on the LCD and in the serial print. However, if the LCD backlight is ON and the temperature changes at the sensor, the new temperature value is not displayed on the LCD, just the temperature prior to the backlight going off but is showing the new value in the serial print. As soon as the LCD backlight times out, the new correct temperature is displayed on the LCD.
I wondered if it was something to do with the extra power drawn by the backlight and moved the LCD GND wire to a seperate GND socket. I have tried increasing the supply to the Arduino from 8.0volts to 10.0volts with no change.
Any thoughts please as to what to look at next would be most appreciated.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 9/10/2024
 Description : A project to display the current date, time and temperature on the 20x4 LCD screen
 and create a programmable thermostatic heating control of three zones to include frost protection and override facilities.
 This has been loosely based on the RTClib ds3231 example along with many code parts borrowed
 from several other files/sketches and in collaboration with several authors from the
 Arduino Forum in particular @alto777, @blh64, @cattledog, @StefanL38 and @UKHeliBob. */
#include "RTClib.h"
#include <OneWire.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <DST_RTC.h>

#define ONE_WIRE_BUS_1 3
#define ONE_WIRE_BUS_2 4
#define ONE_WIRE_BUS_3 5
#define TEMPERATURE_PRECISION 11  // 0.125deg resolution
#define buzzer_pin A0

OneWire oneWire_range(ONE_WIRE_BUS_1);
OneWire oneWire_club(ONE_WIRE_BUS_2);
OneWire oneWire_airgun(ONE_WIRE_BUS_3);

const byte range_relay = 10;  // Relay outputs
const byte club_relay = 11;
const byte airgun_relay = 12;
const byte boiler_relay = 13;

float range_temperature;
float club_temperature;
float airgun_temperature;

float range_temperature_setpoint;   // C
float club_temperature_setpoint;    // C
float airgun_temperature_setpoint;  // C
const int frost_setpoint = 12.0;    // C
const float deadzone = 1.0;         // C

ezButton leftbutton(6);   // create ezButton object that is attached to pin 6;
ezButton upbutton(7);     // create ezButton object that is attached to pin 7;
ezButton downbutton(8);   // create ezButton object that is attached to pin 8;
ezButton rightbutton(9);  // create ezButton object that is attached to pin 9;

const int ledPin = 13;

const unsigned menuOnTime = 20000;
const unsigned upButtonTime = 50;
const unsigned downButtonTime = 50;
const unsigned printInterval = 3000;
const unsigned overrideRunTime = 60000;
const unsigned backlightOnTime = 30000;

int value = 0;
int selector = 0;

int EEPROM_address_1 = 0;  // Address of the location of range setpoint temperature
int EEPROM_address_2 = 4;  // Address of the location of club setpoint temperature
int EEPROM_address_3 = 8;  // Address of the location of airgun setpoint temperature

unsigned long startMillis = 0;
unsigned long menuOffTime = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long backlightOffTime = 0;
unsigned long previousMillisMenu = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisOverrideStart = 0;
unsigned long previousMillisOverrideFinish = 0;
unsigned long previousMillisAdjustSetpointUp = 0;
unsigned long previousMillisAdjustSetpointDown = 0;

bool menuOn = false;                // if = True initiiates the setup menu
bool isRangeHeatOn = false;         // if = True this allows the Range heating
bool isClubHeatOn = false;          // if = True this allows the Club heating
bool isAirgunHeatOn = false;        // if = True this allows the Airgun heating
bool setpointSave = false;          // if = True allows the new setpoints to be saved to EEPROM
bool heatingOverride = false;       // if = True allows the heating override facility
bool adjustSetpointUp = false;      // if = True allows the Setpoint increment
bool adjustSetpointDown = false;    // if = True allows the Setpoint decrement
bool rangeSetpointAdjust = false;   // if = True allows the range setpoint to be adjusted
bool clubSetpointAdjust = false;    // if = True allows the club setpoint to be adjusted
bool airgunSetpointAdjust = false;  // if = True allows the airgun setpoint to be adjusted

DallasTemperature range_sensor(&oneWire_range);
DallasTemperature club_sensor(&oneWire_club);
DallasTemperature airgun_sensor(&oneWire_airgun);

enum { SUNDAY,
       MONDAY,
       TUESDAY,
       WEDNESDAY,
       THURSDAY,
       FRIDAY,
       SATURDAY };

const int On_Normal_Range = 16 * 60UL + 00;    // 16:00 Normal ON time
const int Off_Normal_Range = 21 * 60UL + 30;   // 21:30 Normal OFF time
const int On_Normal_Club = 16 * 60UL + 00;     // 16:00 Normal ON time
const int Off_Normal_Club = 21 * 60UL + 30;    // 21:30 Normal OFF time
const int On_Normal_Airgun = 16 * 60UL + 00;   // 16:00 Normal ON time
const int Off_Normal_Airgun = 21 * 60UL + 30;  // 21:30 Normal OFF time
const int On_Sunday = 12 * 60UL + 00;          // 12:00 Sunday ON time
const int Off_Sunday = 17 * 60UL + 30;         // 17:30 Sunday OFF time

LiquidCrystal_I2C lcd(0X27, 20, 4);
RTC_DS3231 rtc;
DST_RTC dst_rtc;  // DST object

// Define US or EU rules for DST comment out as required. More countries could be added with different rules in DST_RTC.cpp
// const char rulesDST[] = "US"; // US DST rules
const char rulesDST[] = "EU";  // EU DST rules

char daysOfTheWeek[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
byte char_temp[8] = { B00100, B01010, B01010, B01110, B01110, B11111, B11111, B01110 };  // for thermometer icon

void setup() {

  pinMode(range_relay, OUTPUT);
  pinMode(club_relay, OUTPUT);
  pinMode(airgun_relay, OUTPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(buzzer_pin, OUTPUT);

  leftbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  upbutton.setDebounceTime(50);     // set debounce time to 50 milliseconds
  downbutton.setDebounceTime(50);   // set debounce time to 50 milliseconds
  rightbutton.setDebounceTime(50);  // set debounce time to 50 milliseconds

  Serial.begin(9600);

  // EEPROM.put(EEPROM_address_1, range_temperature_setpoint);   // write the float value to EEPROM ONLY used to initially set up setpoint
  // EEPROM.put(EEPROM_address_2, club_temperature_setpoint);    // write the float value to EEPROM ONLY used to initially set up setpoint
  // EEPROM.put(EEPROM_address_3, airgun_temperature_setpoint);  // write the float value to EEPROM ONLY used to initially set up setpoint

  RetrieveSetpoint();

  startMillis = millis();

  Wire.begin();
  rtc.begin();

  lcd.init();
  lcd.backlight();
  lcd.createChar(0, char_temp);
  lcd.setCursor(2, 0);
  lcd.print("G0RJM Three Zone");
  lcd.setCursor(1, 1);
  lcd.print("Temperature Control");
  lcd.setCursor(4, 2);
  lcd.print("Take it easy!");
  lcd.setCursor(4, 3);
  lcd.print("Working on it");

  delay(1000);
  lcd.clear();
  if (rtc.lostPower()) {
    Serial.println("RTC is NOT running!");
    // Uncomment the following line to set the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // rtc.adjust(DateTime(2024,4,24,21,49,00));
    /* DST? If we're in it, let's subtract an hour from the RTC time to keep our DST calculation correct. This gives us
    Standard Time which our DST check will add an hour back to if we're in DST.
    */
    DateTime standardTime = rtc.now();
    if (dst_rtc.checkDST(standardTime) == true) {  // check whether we're in DST right now. If we are, subtract an hour.
      standardTime = standardTime.unixtime() - 3600;
    }
    rtc.adjust(standardTime);
  }

  Serial.println("G0RJM Three zone Temperature Control");

  range_sensor.begin();
  club_sensor.begin();
  airgun_sensor.begin();

  range_sensor.setResolution(TEMPERATURE_PRECISION);
  club_sensor.setResolution(TEMPERATURE_PRECISION);
  airgun_sensor.setResolution(TEMPERATURE_PRECISION);

}  // End of setup

void loop() {

  //       ("ABCDEFGHIJ1234567890")

  //-------------------------Button control---------------------------//

  leftbutton.loop();   // MUST call the loop() function first
  upbutton.loop();     // MUST call the loop() function first
  downbutton.loop();   // MUST call the loop() function first
  rightbutton.loop();  // MUST call the loop() function first

  // -----------------------Backlight display-------------------------//

  unsigned long currentMillis = millis();  // all time elements are data type unsigned long

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed())) {
    TemperatureDisplay();
    menuOn = false;
  }

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed() || (leftbutton.isPressed() || (upbutton.isPressed() || (downbutton.isPressed()))))) {
    lcd.backlight();
    backlightOffTime = currentMillis + backlightOnTime;
  }

  if (currentMillis >= backlightOffTime) {
    lcd.noBacklight();
    TemperatureDisplay();
    menuOn = false;
  }
  //---------------------------Heating Override-----------------------//

  if ((downbutton.isPressed() && (!menuOn) && (!heatingOverride))) {
    for (int n = 0; n < 1; n++) {
      if (currentMillis - previousMillisOverrideFinish >= 10000) {
        heatingOverride = true;  // Establishes the heating override
        isRangeHeatOn = true;
        isClubHeatOn = true;
        isAirgunHeatOn = true;
        HeatingAvailable();
        Serial.println("Heating Override On");
        lcd.backlight();
        lcd.setCursor(0, 0);
        lcd.print("   OVERRIDE IS ON   ");
        backlightOffTime = currentMillis + overrideRunTime;
        previousMillisOverrideStart = currentMillis;
      }
    }
  }

  if (heatingOverride) {
    if (currentMillis - previousMillisOverrideStart >= overrideRunTime)  // Cancels the override
    {
      HeatingOverrideComplete();
      previousMillisOverrideFinish = currentMillis;
    }
  }

  if ((downbutton.isPressed() && (!menuOn) && (heatingOverride)))  // Manually cancels the override
  {
    for (int n = 0; n < 1; n++) {
      if (currentMillis - previousMillisOverrideStart >= 10000) {
        HeatingOverrideComplete();
        previousMillisOverrideFinish = currentMillis;
      }
    }
  }

  //------------------------Boiler Interlock--------------------------//

  if ((digitalRead(range_relay) == HIGH) || (digitalRead(club_relay) == HIGH) || (digitalRead(airgun_relay) == HIGH)) {
    digitalWrite(boiler_relay, HIGH);
  } else {
    digitalWrite(boiler_relay, LOW);
  }

  //------------------------------Menu--------------------------------//

  if (leftbutton.isPressed()) {
    menuOn = true;  // Initiiates the setup menu
    SettingMenu();
    selector++;  // this is done after SettingMenu(), so case 0 will be executed on the first button press
    if (selector > 5) {
      selector = 0;
    }
    previousMillisMenu = currentMillis;
  }

  if (menuOn) {
    if (currentMillis - previousMillisMenu >= menuOnTime) {
      menuOn = false;
      selector = 6;
      delay(2000);
      TemperatureDisplay();
    }
  }

  //------------------------------------------------------------------//

  if (upbutton.isPressed()) {
    adjustSetpointUp = true;  // Setpoint adjustment upwards is available
    previousMillisAdjustSetpointUp = currentMillis;
  }

  if (adjustSetpointUp) {
    if (currentMillis - previousMillisAdjustSetpointUp >= upButtonTime) {
      adjustSetpointUp = false;
    }
  }

  //------------------------------------------------------------------//

  if (adjustSetpointUp && rangeSetpointAdjust)  // If True, this allows the Range setpoint to be adjusted
  {
    range_temperature_setpoint += 0.1;  // Range Setpoint increment
    Serial.println("range_temperature_setpoint_up");
    RangeDisplay();
    Buzzer();
  }

  if (adjustSetpointUp && clubSetpointAdjust)  // If True, this allows the Club setpoint to be adjusted
  {
    club_temperature_setpoint += 0.1;  // Club Setpoint increment
    Serial.println("club_temperature_setpoint_up");
    ClubDisplay();
    Buzzer();
  }

  if (adjustSetpointUp && airgunSetpointAdjust)  // If True, this allows the Airgun setpoint to be adjusted
  {
    airgun_temperature_setpoint += 0.1;  // Airgun Setpoint increment
    Serial.println("airgun_temperature_setpoint_up");
    AirgunDisplay();
    Buzzer();
  }

  //------------------------------------------------------------------//

  if (downbutton.isPressed()) {
    adjustSetpointDown = true;  // Setpoint adjustment Downwards is available
    previousMillisAdjustSetpointDown = currentMillis;
  }
  if (adjustSetpointDown) {
    if (currentMillis - previousMillisAdjustSetpointDown >= downButtonTime) {
      adjustSetpointDown = false;
    }
  }

  if (adjustSetpointDown && rangeSetpointAdjust)  // If True, this allows the Range setpoint to be adjusted
  {
    range_temperature_setpoint -= 0.1;  // Range Setpoint decrement
    Serial.println("range_temperature_setpoint_down");
    RangeDisplay();
    Buzzer();
  }

  if (adjustSetpointDown && clubSetpointAdjust)  // If True, this allows the Club setpoint to be adjusted
  {
    club_temperature_setpoint -= 0.1;  // Club Setpoint decrement
    Serial.println("club_temperature_setpoint_down");
    ClubDisplay();
    Buzzer();
  }

  if (adjustSetpointDown && airgunSetpointAdjust)  // If True, this allows the Airgun setpoint to be adjusted
  {
    airgun_temperature_setpoint -= 0.1;  // Airgun Setpoint decrement
    Serial.println("airgun_temperature_setpoint_down");
    AirgunDisplay();
    Buzzer();
  }


  //-----------------------Setpoint Save------------------------------//

  if (setpointSave && adjustSetpointUp && adjustSetpointDown)  // If True, the new temperature setpoints are saved to EEPROM
  {
    for (int n = 0; n < 1; n++) {
      Serial.println("Changes saved");
      EEPROM.put(EEPROM_address_1, range_temperature_setpoint);   // write the new range setpoint value to EEPROM, only if it has changed
      EEPROM.put(EEPROM_address_2, club_temperature_setpoint);    // write the new club setpoint value to EEPROM, only if it has changed
      EEPROM.put(EEPROM_address_3, airgun_temperature_setpoint);  // write the new airgun setpoint value to EEPROM, only if it has changed

      delay(2000);
      RetrieveSetpoint();
      lcd.setCursor(3, 3);
      lcd.print("Settings saved");
      delay(3000);
      lcd.clear();
      TemperatureDisplay();
      HeatingAvailable();
    }
  }
  //----------------------------Print delay---------------------------//

  if (currentMillis - previousMillis >= printInterval)  // has enough time passed yet?
  {
    previousMillis = previousMillis + printInterval;  // If yes, set the time del;ay for the next print event
                                                      // capture & print data, or print latest data, etc.
                                                      // the closing curly brace at end of loop.

    //--------------------Daylight Saving Setting---------------------//

    DateTime standardTime = rtc.now();

    Serial.println("Standard Time");
    printTheTime(standardTime);

    DateTime theTime = dst_rtc.calculateTime(standardTime);  // takes into account DST

    Serial.println("Time adjusted for Daylight Saving Time");
    printTheTime(theTime);


    //-----------------------Timeswitch Control-----------------------//

    int currentTime = theTime.hour() * 60UL + theTime.minute();

    //--------------------Timed heating setting-----------------------//

    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to Saturday so use normal times
      if (currentTime < On_Normal_Range || currentTime > Off_Normal_Range) {
        // this is outside normal range heating time
        isRangeHeatOn = false;
      } else {
        // this is within normal range heating time
        isRangeHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // this is outside Sunday range heating time
        isRangeHeatOn = false;
      } else {
        // this is within Sunday range heating time
        isRangeHeatOn = true;
      }
    }
    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to Saturday so use normal times
      if (currentTime < On_Normal_Club || currentTime > Off_Normal_Club) {
        // this is outside normal club heating time
        isClubHeatOn = false;
      } else {
        // this is within normal club heating time
        isClubHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // this is outside Sunday club heating time
        isClubHeatOn = false;
      } else {
        // this is within Sunday club heating time
        isClubHeatOn = true;
      }
    }
    if (theTime.dayOfTheWeek() != SUNDAY) {
      // Monday to Saturday so use normal times
      if (currentTime < On_Normal_Airgun || currentTime > Off_Normal_Airgun) {
        // this is outside normal airgun heating time
        isAirgunHeatOn = false;
      } else {
        // this is within normal airgun heating time
        isAirgunHeatOn = true;
      }
    } else {
      // Sunday times
      if (currentTime < On_Sunday || currentTime > Off_Sunday) {
        // this is outside Sunday airgun heating time
        isAirgunHeatOn = false;
      } else {
        // this is within Sunday airgun heating time
        isAirgunHeatOn = true;
      }
    }

    if ((rightbutton.isPressed() || (!menuOn && !adjustSetpointUp && !adjustSetpointDown && !heatingOverride))) {

      HeatingAvailable();


      //---------------------Date & Time display----------------------//

      lcd.setCursor(0, 0);
      lcd.print(daysOfTheWeek[theTime.dayOfTheWeek()]);
      lcd.setCursor(3, 0);
      lcd.print(":");
      lcd.setCursor(4, 0);
      if (theTime.day() < 10) lcd.print('0');
      lcd.print(theTime.day(), DEC);
      lcd.print(':');
      if (theTime.month() < 10) lcd.print('0');
      lcd.print(theTime.month(), DEC);
      lcd.print(':');
      lcd.print(theTime.year(), DEC);
      lcd.setCursor(15, 0);
      if (theTime.hour() < 10) lcd.print('0');
      lcd.print(theTime.hour(), DEC);
      lcd.print(':');
      if (theTime.minute() < 10) lcd.print('0');
      lcd.print(theTime.minute(), DEC);
    }

    //------------------------Serial print---------------------------//

    Serial.print("Requesting temperatures...");
    range_sensor.requestTemperatures();
    club_sensor.requestTemperatures();
    airgun_sensor.requestTemperatures();
    Serial.println(" done");
    Serial.print("Range: ");
    Serial.println(range_sensor.getTempCByIndex(0));
    Serial.print("Club: ");
    Serial.println(club_sensor.getTempCByIndex(0));
    Serial.print("Airgun: ");
    Serial.println(airgun_sensor.getTempCByIndex(0));
  }

}  // End of loop

//----------------------Print time to serial-------------------------//
void printTheTime(DateTime theTimeP) {

  Serial.print(theTimeP.year(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.month(), DEC);
  Serial.print('/');
  Serial.print(theTimeP.day(), DEC);
  Serial.print(' ');
  Serial.print(theTimeP.hour(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.minute(), DEC);
  Serial.print(':');
  Serial.print(theTimeP.second(), DEC);
  Serial.println();
}

//---------------------------Buzzer----------------------------------//
void Buzzer() {

  {
    digitalWrite(buzzer_pin, HIGH);
    delay(100);
    digitalWrite(buzzer_pin, LOW);
  }
}
//------------------------Retrieve setpoint--------------------------//
void RetrieveSetpoint() {

  EEPROM.get(EEPROM_address_1, range_temperature_setpoint);   //retrieve the range setpoint from EEPROM
  EEPROM.get(EEPROM_address_2, club_temperature_setpoint);    //retrieve the club setpoint from EEPROM
  EEPROM.get(EEPROM_address_3, airgun_temperature_setpoint);  //retrieve the airgun setpoint from EEPROM

  Serial.print("Range Temperature Setpoint  - ");
  Serial.print(range_temperature_setpoint);
  Serial.print(" Stored at addr ");
  Serial.println(EEPROM_address_1);
  Serial.print("Club Temperature Setpoint   - ");
  Serial.print(club_temperature_setpoint);
  Serial.print(" Stored at addr ");
  Serial.println(EEPROM_address_2);
  Serial.print("Airgun Temperature Setpoint - ");
  Serial.print(airgun_temperature_setpoint);
  Serial.print(" Stored at addr ");
  Serial.println(EEPROM_address_3);
}

//------------------------Setting Menu-------------------------------//
void SettingMenu() {

  switch (selector) {
    case 0:
      Serial.println("Case 0");  // General menu
      MenuScreen();
      break;
    case 1:
      Serial.println("Case 1");  // Displays the Range setpoint
      RangeDisplay();
      Buzzer();
      break;
    case 2:
      Serial.println("Case 2");  // Displays the Club setpoint
      ClubDisplay();
      Buzzer();
      break;
    case 3:
      Serial.println("Case 3");  // Displays the Airgun setpoint
      AirgunDisplay();
      Buzzer();
      break;
    case 4:
      Serial.println("Case 4");  // Instructs how to save the new settings
      SaveDisplay();
      break;
    case 5:
      Serial.println("Case 5");  // Reminds you to save
      lcd.clear();
      lcd.setCursor(3, 2);
      lcd.print("Did you save?");
      Buzzer();
  }
}

//-------------------------Menu screen-------------------------------//
void MenuScreen() {

  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
  setpointSave = false;
}

//-----------------------Setting Displays---------------------------//
void RangeDisplay() {

  rangeSetpointAdjust = true;
  clubSetpointAdjust = false;
  airgunSetpointAdjust = false;
  Serial.println("RangeChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 1);
  lcd.print("Range: ");
  lcd.print(range_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

//-------------------------------------------------------------------//
void ClubDisplay() {

  clubSetpointAdjust = true;
  rangeSetpointAdjust = false;
  airgunSetpointAdjust = false;
  Serial.println("ClubChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 2);
  lcd.print("Club:  ");
  lcd.print(club_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

//-------------------------------------------------------------------//
void AirgunDisplay() {

  airgunSetpointAdjust = true;
  rangeSetpointAdjust = false;
  clubSetpointAdjust = false;
  Serial.println("AirgunChange");
  lcd.clear();
  lcd.print(" Setting Menu:      ");
  lcd.setCursor(0, 3);
  lcd.print("Airgun:");
  lcd.print(airgun_temperature_setpoint, 1);
  lcd.write((char)223);
  lcd.print('C');
}

//------------------------Save Display-------------------------------//
void SaveDisplay() {

  rangeSetpointAdjust = false;
  clubSetpointAdjust = false;
  airgunSetpointAdjust = false;
  Serial.println("SavingChanges");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("To save the changes");
  lcd.setCursor(2, 1);
  lcd.print("Press Up & Down");
  lcd.setCursor(5, 2);
  lcd.print("Together");
  setpointSave = true;
}

//---------------------Temperature display - ------------------------//
void TemperatureDisplay() {

  lcd.setCursor(0, 1);  // Range Temperature
  lcd.print("Range: ");
  lcd.print(range_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 2);  // Club Temperature
  lcd.print("Club:  ");
  lcd.print(club_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');

  lcd.setCursor(0, 3);  // Airgun Temperature
  lcd.print("Airgun:");
  lcd.print(airgun_sensor.getTempCByIndex(0), 1);
  lcd.write((char)223);
  lcd.print('C');
}

//--------------------Heating Override Complete----------------------//
void HeatingOverrideComplete() {

  heatingOverride = false;
  isRangeHeatOn = false;
  isClubHeatOn = false;
  isAirgunHeatOn = false;
  lcd.setCursor(0, 0);
  lcd.print("   OVERRIDE IS OFF   ");
  delay(2000);
  lcd.noBacklight();
  Serial.println("Heating Override complete");
}

//-----------------------Heating Available---------------------------//
void HeatingAvailable() {

  RangeHeatingAvailable();
  ClubHeatingAvailable();
  AirgunHeatingAvailable();
}

//-------------------------------------------------------------------//
void RangeHeatingAvailable() {
  {
    if (isRangeHeatOn) {
      NormalHeatingRange();
      Serial.println("Heat R");
    } else {
      Serial.println("Frost Prev. R");
      FrostPreventionRange();
    }
  }
}

//-------------------------------------------------------------------//
void ClubHeatingAvailable() {
  {
    if (isClubHeatOn) {
      NormalHeatingClub();
      Serial.println("Heat C");
    } else {
      Serial.println("Frost Prev. C");
      FrostPreventionClub();
    }
  }
}

//-------------------------------------------------------------------//
void AirgunHeatingAvailable() {
  {
    if (isAirgunHeatOn) {
      NormalHeatingAirgun();
      Serial.println("Heat A");
    } else {
      Serial.println("Frost Prev. A");
      FrostPreventionAirgun();
    }
  }
}

//-------------------Frost Prevention Range--------------------------//
void FrostPreventionRange() {

  if ((range_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.println("Ra Heating");
    lcd.setCursor(13, 1);
    lcd.print("  ON  F");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      lcd.setCursor(13, 1);
      lcd.print("  OFF F");
    }
  }
}

//--------------------Frost Prevention Club-------------------------//
void FrostPreventionClub() {

  if ((club_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.println("Cl Heating");
    lcd.setCursor(13, 2);
    lcd.print("  ON  F");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      lcd.setCursor(13, 2);
      lcd.print("  OFF F");
    }
  }
}

//---------------------Frost Prevention Airgun-----------------------//
void FrostPreventionAirgun() {

  if ((airgun_sensor.getTempCByIndex(0) <= (frost_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.println("Ai Heating");
    lcd.setCursor(13, 3);
    lcd.print("  ON  F");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (frost_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      lcd.setCursor(13, 3);
      lcd.print("  OFF F");
    }
  }
}

//-------------------Normal Heating Range----------------------------//
void NormalHeatingRange() {

  if ((range_sensor.getTempCByIndex(0) <= (range_temperature_setpoint - deadzone))) {
    digitalWrite(range_relay, HIGH);
    Serial.println("Heating R");
    lcd.setCursor(13, 1);
    lcd.print("  ON  T");
  } else {
    if ((range_sensor.getTempCByIndex(0) >= (range_temperature_setpoint + deadzone))) {
      digitalWrite(range_relay, LOW);
      lcd.setCursor(13, 1);
      lcd.print("  OFF T");
    }
  }
}

//--------------------Normal Heating Club ---------------------------//
void NormalHeatingClub() {

  if ((club_sensor.getTempCByIndex(0) <= (club_temperature_setpoint - deadzone))) {
    digitalWrite(club_relay, HIGH);
    Serial.println("Heating C");
    lcd.setCursor(13, 2);
    lcd.print("  ON  T");
  } else {
    if ((club_sensor.getTempCByIndex(0) >= (club_temperature_setpoint + deadzone))) {
      digitalWrite(club_relay, LOW);
      lcd.setCursor(13, 2);
      lcd.print("  OFF T");
    }
  }
}

//---------------------Normal Heating Airgun-------------------------//
void NormalHeatingAirgun() {

  if ((airgun_sensor.getTempCByIndex(0) <= (airgun_temperature_setpoint - deadzone))) {
    digitalWrite(airgun_relay, HIGH);
    Serial.println("Heating A");
    lcd.setCursor(13, 3);
    lcd.print("  ON  T");
  } else {
    if ((airgun_sensor.getTempCByIndex(0) >= (airgun_temperature_setpoint + deadzone))) {
      digitalWrite(airgun_relay, LOW);
      lcd.setCursor(13, 3);
      lcd.print("  OFF T");
    }
  }
}

I'm looking through the tiny window, but this looks odd

  if ((currentMillis + backlightOffTime <= 3000) || (rightbutton.isPressed() || (leftbutton.isPressed() || (upbutton.isPressed() || (downbutton.isPressed()))))) {
    lcd.backlight();
    backlightOffTime = currentMillis + backlightOnTime;
  }

  if (currentMillis >= backlightOffTime) {
    lcd.noBacklight();
    TemperatureDisplay();
    menuOn = false;
  }

When you turn off the backlight, you call TemperatureDisplay().

When you turn on the backlight, you do not.

Again, just looking for why two things that should be totally independent are not.

FWIW, this may mean the sketch is a structural jumble. If the separate functions of the sketch were, well, separate, there would be no real possibility of, at least software-wise, these things cross contaminating each other.

The power idea is worth looking into, just replace turning on and off the backlight with serial messages saying you will do one day, and leave the backlight programmed off. Or disconnect the backlight drive signal.

HTH

a7