Trying to get millis to work with button

Evening all, I am at the next stage of my central heating control project and I am now trying to incorporate four buttons to be able to change some of the temperature parameters. However before getting way too complicated I am trying to set up a single button to provide simple function of printing to the LCD and serial print.
I have decided not to use a debounce library and have follwed the online advice from UKHeliBob.
I am sure that I have mistyped or not completely understand his very good article.
Here is the code that I am working on. There are several items in the global area that refer to more of the project.
Any thoughts or suggestions would be most appreciated.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 19/06/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 <DST_RTC.h>
#include <DallasTemperature.h>
#include <EEPROM.h>

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

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

const byte leftbutton = 6;
const int upbutton = 7;
const int downbutton = 8;
const int rightbutton = 9;
const int ledPin = 13;

const unsigned printInterval = 3000;
const unsigned backlightOnTime = 10000;
const unsigned menuOnTime = 20000;

int buttonState = 0;
int lastButtonState = 0;    // the previous reading from the input pin
int buttonPushCounter = 0;  // counter for the number of button presses

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

unsigned long startMillis = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisMenu = 0;
unsigned long periodStartMillis = 0;

const unsigned long period = 5000;

byte currentButtonState;
byte previousButtonState;
int count = 0;
boolean printFinalMessage = true;

unsigned long debounceStartMillis;
unsigned long debouncePeriod = 20;
boolean debouncing = false;

bool backlightOn = false;
bool menuOn = 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(leftbutton, INPUT_PULLUP);
  pinMode(upbutton, INPUT_PULLUP);
  pinMode(downbutton, INPUT_PULLUP);
  pinMode(rightbutton, INPUT_PULLUP);

  Serial.begin(9600);

  startMillis = millis();

  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(ledPin, OUTPUT);

}  // End of setup

void loop() {

  //----------------------------Button control-----------------------//
  currentMillis = millis();
  if (currentMillis - periodStartMillis <= period)  //true until the period elapses
  {
    previousButtonState = currentButtonState;       //save the previous button state
    currentButtonState = digitalRead(leftbutton);   //read the current state of the input
    if (currentButtonState != previousButtonState)  //if the button state has changed
    {
      debounceStartMillis = currentMillis;                      //save the time that the state change occured
      debouncing = true;                                        //flag that debouncing in progress
    }                                                           //end state change check
    if (currentMillis - debounceStartMillis >= debouncePeriod)  //if the debounce period has elapsed
    {
      if (debouncing == true)  //debouncing taking place
      {
        if (currentButtonState == LOW)  //if the button is currently pressed
        {
          debouncing = false;  //debouncing is finished
          digitalWrite(ledPin, HIGH);
          Serial.println("ON");
          lcd.setCursor(0, 2);
          lcd.print("  ON  ");

        } else {
          // if the current state is HIGH then the button went from on to off:
          digitalWrite(ledPin, HIGH);
          Serial.println("Off");
          lcd.setCursor(0, 2);
          lcd.print("  OFF  ");
        }
      }
    }  
  }
}

Do you have a problem with the sketch or are you just asking for comments and/or possible improvements ?

It would help if you explained what the sketch is supposed to do

Does it work?

The entire loop is in an if statement which can run only for the first 5000 milliseconds since startup.

I changed that if test to

  if (1) {  // let this body run freely for all time

HIGH is the only value ever written to the LED. I made the obvious correction.


Also missing is a symmetrical cancelling of the debouncing, I wrote
        if (currentButtonState == LOW)
        {
          debouncing = false;  //debouncing is finished
          digitalWrite(ledPin, HIGH);
          Serial.println("ON");
        } else {
          debouncing = false;  //debouncing is finished HERE TOO
          digitalWrite(ledPin, LOW);
          Serial.println("Off");
        }

I have stripped out the irrelevant junk and left the logic, so no LCD for example.

With those changes, the button crisply reports ON (once!) when you press it, and reports Off (once!) when you release it. The LED appears to be on whilst you are pressing, and off when you are not.

S'long way to go to have the LED follow the pushbutton input, so I wonder if that is what is being aimed for here.

A single delay of 20 milliseconds at the end of the loop can effectively debounce all the buttons you use, leaving only the state change detection for you to do.

Depending on how you leverage this code (once it does what you want) the global poor man's debouncing might make things simpler.

And unless you have some particular reason to avoid it, ezButton as you were once using or trying to would def make things very simpler.

Hand debouncing is something I think everyone should know how to do. But that's me. ezButton gets my highest praise it doesn't suck and I use it when I don't care about the parts of it that don't suit me. It works.

a7

1 Like

Thank you for all your speedy replies.
What I was hoping to achieve with the button push was to be the initiation of several case statements.
I thought it would be best to get the single button operating with debounce and a timed sequence before releasing this action back to default.
What I found was that the code stayed in the default mode displaying “Off” at serial print irrespective of any button pushes.
Hence the late night request for help.
Probably an early evening brain might have been able to work out my errors.
I think that I have over complicated things and will have to put a lot more thought into what I want to achieve.
I was trying to code the debounce without using a library.
Back to the thought process.

Alto777, I have corrected the late evening errors caused by too much 'cut-n-paste'!
I have made some good progress following your advice.
I now have the code defaulting to OFF until the button is pushed, which shows ON and then returning to OFF when the button is released.
What I am now trying to achieve is a specified time delay after the button is released but before the code returns to the default OFF in the serial print.
I have commented out the line 191 which shows my incorrect thoughts!
Please see the updated code below. Any thoughts or suggestions would be most welcome.

* Author : John Marchant G0RJM
 Created : 25/01/2024 - 15/06/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 <DST_RTC.h>
#include <DallasTemperature.h>
#include <EEPROM.h>

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

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

const int leftbutton = 6;
const int upbutton = 7;
const int downbutton = 8;
const int rightbutton = 9;
const int ledPin = 13;

const unsigned printInterval = 3000;
const unsigned backlightOnTime = 10000;
const unsigned menuOnTime = 20000;

int buttonState = 0;
int lastButtonState = 0;    // the previous reading from the input pin
int buttonPushCounter = 0;  // counter for the number of button presses

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 20;    // the debounce time; increase if the output flickers

unsigned long startMillis = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisMenu = 0;
unsigned long periodStartMillis = 0;
unsigned long menuMillis = 0;
unsigned long menuStartMillis = 0;
unsigned long debounceStartMillis = 0;
unsigned long debouncePeriod = 20;
unsigned long menuPeriod = 10000;

const unsigned long period = 10000;

byte currentButtonState;
byte previousButtonState;
int count = 0;

boolean debouncing = false;

bool backlightOn = false;
bool menuOn = 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);

  pinMode(leftbutton, INPUT_PULLUP);
  pinMode(upbutton, INPUT_PULLUP);
  pinMode(downbutton, INPUT_PULLUP);
  pinMode(rightbutton, INPUT_PULLUP);

  Serial.begin(9600);

  startMillis = millis();

  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();

  // periodStartMillis = millis();

}  // End of setup

void loop() {

  currentMillis = millis();

  {
    previousButtonState = currentButtonState;       //save the previous button state
    currentButtonState = digitalRead(leftbutton);   //read the current state of the input
    if (currentButtonState != previousButtonState)  //if the button state has changed
    {
      debounceStartMillis = currentMillis;                      //save the time that the state change occured
      debouncing = true;                                        //flag that debouncing in progress
    }                                                           //end state change check
    if (currentMillis - debounceStartMillis >= debouncePeriod)  //if the debounce period has elapsed
    {
      if (debouncing == true)  //debouncing taking place
      {
        if (currentButtonState == LOW)  //if the button is currently pressed
        {
          debouncing = false;  //debouncing is finished
          digitalWrite(ledPin, HIGH);
          Serial.println("ON");
          lcd.setCursor(0, 2);
          lcd.print("  ON  ");

        } else {

          menuStartMillis = currentMillis;
          debouncing = false;  //debouncing is finished

          // if (currentMillis - menuStartMillis >= menuPeriod) {
          Serial.println("Off");
          lcd.setCursor(0, 2);
          lcd.print("  OFF  ");
          //}
        }
      }
    }
  }
}

OK. This gets execute once when the button becomes pressed:

          Serial.println("ON");
          lcd.setCursor(0, 2);
          lcd.print("  ON  ");

and this gets execute once when the button is released.:

          Serial.println("Off");
          lcd.setCursor(0, 2);
          lcd.print("  OFF  ");

If I understand, you could turn on the LED when you print ON, and at the same time start a timer.

Recall or learn that starting a timer in this context is simply noting the time. Here we assume the existence of variables in scope (global is easiest for now):

bool lightIsOn;
unsigned long ledTimer;

so where you print ON we just

    ledTimer = millis();
    digitalWrite(ledPin, HIGH);
    lightIsOn = true;
    

or if your sketch has it being set in the loop as is recommended and commonly done in unblocked code, currentMillis, set to millis() at the top of the loop. I use the less important sounding now as the name for that unsigned long "clock" reading.

Do nothing with the LED in the section that is recognizing the button becoming unpressed.

Elsewhere in the loop, in totally independent section of code, check if the timer is running (lightIsOn) and the timer has expired.

Recall or learn that to check a timer for expiry is simply to see if the elapsed time has gone past however long you want the LED to be on:

  if (lightIsOn && millis() - ledTimer > 777) {
    lightIsOn = false;
    digitalWrite(ledPin, LOW);  
  }

would keep the LED on for 777 milliseconds, a number I picked totally at random and can be, obvsly I hope, tailored to suit your own needs.

I'm at the beach, and typing this on not my tablet, so I am afraid I can neither test this nor present it to you in the context of your, or a, complete sketch.

See if it makes sense, meet me more than halfway. I can't say when I will check. We are roasting here, TBH I won't be surprised if this is all the product of my sun-struck brain, or what is left of it. Drinking isn't helping… not like it usually does. :expressionless:

HTH

a7

Evening @alto777, thank you for you reply and suggestions. I have modified my sketch accordingly and this time looked for any simple errors! When I press the buttons the LED illuminates, however it does not switch off after the time poeriod. I guess that I have got part of the code incorrect.
When I dedlcated the bool for lightIsOn in Line 77, I noticed that there is no "= false" should I append this?
Please see my code below. Any further thoughts would be nost appreciated.

/* Author : John Marchant G0RJM
 Created : 25/01/2024 - 19/06/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 <DST_RTC.h>
#include <DallasTemperature.h>
#include <EEPROM.h>

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

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

const int leftbutton = 6;
const int upbutton = 7;
const int downbutton = 8;
const int rightbutton = 9;
const int ledPin = 13;

const unsigned printInterval = 3000;
const unsigned backlightOnTime = 10000;
const unsigned menuOnTime = 20000;

const unsigned long period = 10000;

int buttonState = 0;
int lastButtonState = 0;    // the previous reading from the input pin
int buttonPushCounter = 0;  // counter for the number of button presses

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 20;    // the debounce time; increase if the output flickers

unsigned long startMillis = 0;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long previousMillisBacklight = 0;
unsigned long previousMillisMenu = 0;
unsigned long periodStartMillis = 0;
unsigned long menuMillis = 0;
unsigned long menuStartMillis = 0;
unsigned long debounceStartMillis = 0;
unsigned long debouncePeriod = 20;
unsigned long menuPeriod = 10000;
unsigned long ledTimer;

byte currentButtonState;
byte previousButtonState;
int count = 0;

bool debouncing = false;
bool backlightOn = false;
bool menuOn = false;
bool lightIsOn;

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);

  pinMode(leftbutton, INPUT_PULLUP);
  pinMode(upbutton, INPUT_PULLUP);
  pinMode(downbutton, INPUT_PULLUP);
  pinMode(rightbutton, INPUT_PULLUP);

  Serial.begin(9600);

  startMillis = millis();

  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();

  // periodStartMillis = millis();

}  // End of setup

void loop() {

  currentMillis = millis();

  {
    previousButtonState = currentButtonState;       //save the previous button state
    currentButtonState = digitalRead(leftbutton);   //read the current state of the input
    if (currentButtonState != previousButtonState)  //if the button state has changed
    {
      debounceStartMillis = currentMillis;                      //save the time that the state change occured
      debouncing = true;                                        //flag that debouncing in progress
    }                                                           //end state change check
    if (currentMillis - debounceStartMillis >= debouncePeriod)  //if the debounce period has elapsed
    {
      if (debouncing == true)  //debouncing taking place
      {
        if (currentButtonState == LOW)  //if the button is currently pressed
        {
          debouncing = false;  //debouncing is finished
          ledTimer = millis();
          digitalWrite(ledPin, HIGH);
          lightIsOn = true;
          /* Serial.println("ON");
          lcd.setCursor(0, 2);
          lcd.print("  ON  "); */

        } else {

          debouncing = false;  //debouncing is finished

          if (lightIsOn && millis() - ledTimer > 2000) {
            lightIsOn = false;
            digitalWrite(ledPin, LOW);
            /* Serial.println("Off");
            lcd.setCursor(0, 2);
            lcd.print("  OFF  "); */
          }
        }
      }
    }
  }
}

A global variable without a specific initial value will be zero, or in this case false.

bool lightIsOn;

So file that under "can't hurt, doesn't matter".

I tend to avoid using ink where it is not necessary. Most good readers of code also know that these variables are initialised; occasionally I will write the redundant "= 0" if I am trying to make some kinda point.

This

if (lightIsOn && millis() - ledTimer > 2000) {
            lightIsOn = false;
            digitalWrite(ledPin, LOW);
            /* Serial.println("Off");
            lcd.setCursor(0, 2);
            lcd.print("  OFF  "); */
          }

is fine, but it is not

in totally independent section of code

It should be in the loop() function, but not buried in any other control structure.

You could make it the very first statement, well the second, or the very last:

void loop() {

  currentMillis = millis();

  if (lightIsOn && currentMillis - ledTimer > 2000) {
    lightIsOn = false;
    digitalWrite(ledPin, LOW);
            /* Serial.println("Off");
            lcd.setCursor(0, 2);
            lcd.print("  OFF  "); */
  }

// rest of the free running loop... 

Since loop(), um, loops, first is essentially the same as last, it's all round and round and round.

Aesthetically or logically it may make better reading to do it as the last statement of the loop. But it will turn off the LED no matter.

I'm in transit and once again cannot test this. @ me when/if you get to this and don't succeed, when I am in the lab I can see if there is something else.

HTH

Thanks @alto777, that works a treat. I had not completely understood what was meant by "independent section of the code".
On to the next section of this project, incorporating this section of code to do something else.

Nice.

I don't know the correct term for what I was saying about the independent code, so I asked chatGPT (!) for its take:

In C++, statements that are at the highest syntactic level within a function, as opposed to being nested within other compound statements, are often referred to simply as "statements."


More formally, these statements can be categorized as "top-level statements" or "outermost statements" within the function's scope. These statements include declarations, assignments, function calls, control flow statements (like if, switch, while, etc.), and any other executable code that is directly within the function body and not nested inside another compound statement.

So by that advice, I should have said make the thing that turns off the LED a top-level statement anywhere in the loop() function.

It is worth reiterating that we can add statements at the top level of the loop() and expect them to be executed only if all the statements in the function do not block. We avoid lengthy computations or massive printing and especially the loop-killer which is dealy().

Then all the statements get attention many times a second, in a good loop perhaps many thousands of times a second.

Now if anyone reading this has a better way to say the same thing about "top-level" statements , or one that is proper like you might find if I said it and you googled it, I am all ears.

a7

1 Like