Issue with LCD Display of Time on Timer

Hello-
Having an issue with a fairly simple (I think) project that keeps track of the time a pump is either on or off (controlled by a float switch). Right now the switch is represented by two wires connected to ground and pin 7, and I short them together to represent the float switch being "on" and disconnect them to represent the float switch being off.

The LCD display is not incrementing the seconds properly, but minutes appears to work. The numbers are incrementing properly in the serial printout. I've created a number of subroutines to help process a "seconds" value into a more readable "HH:MM:SS.T" format, and I'm pretty sure the issue lies in those. I've been over it many times but you know how it is trying to debug your own code sometimes... I'm sure I'm missing something. Thanks!

#include "Wire.h"               // Library for I2C communication
#include "LiquidCrystal_I2C.h"  // Library for LCD

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4);  // Change to (0x27,20,4) for 20x4 LCD.
int period=100; //time between increments- currently set to tenths of a second
float secondCounter = 0; //counts number of times the pump comes on- not currently implemented
int floatSwitchPin = 7; //which pin the float switch is connected to

boolean pumpOn=false; // State of the pump- starts as "off" (False) and switches to "on" (True when pin7 is shorted to ground)
boolean lastPumpState=false;

float onTimeAccumulator=0; // total amount of time the pump is on 
float offTimeAccumulator=0; // total amount of time the pump is off


void setup() {
  //start LCD screen
  lcd.init();
  lcd.backlight();
  // Initialize sensor pin
  pinMode(floatSwitchPin, INPUT_PULLUP);
  // Open Serial monitor for debugging
  Serial.begin(9600);
}

void loop() {
  unsigned long time_now = millis();
  if (pumpOn==true) { // Check to see if the pump is on
    onTimeAccumulator=onTimeAccumulator + 0.1; //increase the onTimeAccumulator by 0.1 seconds each loop when pump is on
  }
  else { //pump is off
    offTimeAccumulator=offTimeAccumulator + 0.1; //increase the offTimeAcccumulator by 0.1 seconds each loop when pump is off
  }
  
  //Debugging Serial Print Statements
      printTime(0, 0, onTimeAccumulator);
      printTime(0, 1, offTimeAccumulator);
      Serial.print("onTimeAccumulator=");
      Serial.print(onTimeAccumulator);
      Serial.print("  offTimeAccumulator=");
      Serial.print(offTimeAccumulator);
      Serial.print("  pumpOn=");
      Serial.println(pumpOn);


  while(millis() < time_now + period) { //non blocking delay
      pumpOn=!digitalRead(floatSwitchPin);
  }
}


void printTime(int x, int y, float seconds) { //Convert Seconds into user friendly display of HH:MM:SS.T
  if (secondsToHours(seconds) > 99) { 
    x = x + 1; //If Seconds is bigger than 99 move over one column
  }
  lcd.setCursor(x, y);
  if (secondsToHours(seconds) < 10) { //if hours is less than 10 add a leading 0
    lcd.print("0");
    lcd.setCursor(x + 1, y); //move over one column
  }
  lcd.print(secondsToHours(seconds)); //print number of hours (using subroutine below)
  lcd.setCursor(x + 2, y); //move over 2 columns
  lcd.print(":"); //print the colon
  lcd.setCursor(x + 3, y); //move over 3 columns
  if (secondsToMinutes(seconds) - (secondsToHours(seconds) * 60) < 10) { //if number of minutes is less than 10 add a deading 0
    lcd.print("0");
    lcd.setCursor(x + 4, y); //move over
  }
  lcd.print(secondsToMinutes(seconds) - (secondsToHours(seconds) * 60)); //print number of minutes (minus minutes accounted for as hours)
  lcd.setCursor(x + 5, y); //position cursor
  lcd.print(":"); //print colon
  lcd.setCursor(x + 6, y); // position cursor
  if (secondCounter - ((secondsToMinutes(seconds) * 60) + secondsToHours(seconds) * 3600) < 10) { //if number of seconds (minus seconds accounted for as minutes and as hours) is less than 10 add a leading zero
    lcd.print("0"); //add a leading zeor
    lcd.setCursor(x + 7, y); //move the cursor over
  }
  lcd.print(secondCounter - (((secondsToMinutes(seconds) * 60) + secondsToHours(seconds) * 3600)), 1); //print number of seconds (minus seconds accounted for as hours and minutes)
}

int secondsToMinutes(float seconds) { //converts seconds into minutes
  int minutes;
  minutes = floor(seconds / 60);
  return (minutes);
}

int secondsToHours(float seconds) { //converts seconds into hours
  int hours;
  hours = floor(seconds / 3600);
  return (hours);
}

you should use millis() to manage the time . For extra information and examples look at

you might benefit from studying state machines. Here is a small introduction to the topic: Yet another Finite State Machine introduction

I'd suggest you use one of the numerous button libraries such as Button in easyRun or OneButton or Toggle or EasyButton or Bounce2, ... to manage the float switch.

with all this, your code could become something like this (using Toggle to handle the float switch)

click to see the code
#include "Wire.h"               // Library for I2C communication
#include "LiquidCrystal_I2C.h"  // Library for LCD
#include <Toggle.h>  // https://github.com/Dlloydev/Toggle for handling buttons

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4);  // Change to (0x27,20,4) for 20x4 LCD.
const byte floatSwitchPin = 7; //which pin the float switch is connected to
Toggle floatSwitch;

unsigned long onTime = 0; // in ms
unsigned long startTime = 0; // in ms

enum {PUMP_OFF, PUMP_ON} pumpState = PUMP_OFF;

void printTime(unsigned long t) {
  unsigned long h = t / 3600ul;
  unsigned long m = (t % 3600ul) / 60ul;
  unsigned long s = (t % 3600ul) % 60ul;
  if (h < 10) lcd.write('0');
  lcd.print(h);
  lcd.write(':');
  if (m < 10) lcd.write('0');
  lcd.print(m);
  lcd.write(':');
  if (s < 10) lcd.write('0');
  lcd.print(s);
}

void show() {
  lcd.setCursor(0, 1);
  printTime(onTime / 1000ul);

  lcd.setCursor(12, 1);
  if (pumpState == PUMP_OFF) printTime(0);
  else  printTime((millis() - startTime) / 1000ul);// get rid of the ms
}

void setup() {
  floatSwitch.begin(floatSwitchPin);
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("CUMULATED   ONGOING");
}

void loop() {
  show();
  floatSwitch.poll();
  switch (pumpState) {
    case PUMP_OFF: // check if the float switch is activated
      if (floatSwitch.onPress()) {
        startTime = millis();
        pumpState = PUMP_ON;
      }
      break;

    case PUMP_ON:
      if (floatSwitch.onRelease()) {
        onTime += (millis() - startTime);
        startTime = 0;
        pumpState = PUMP_OFF;
      }
      break;
  }
}


I did not include the 10th of the seconds, left as an exercise for you.

1 Like

How exactly are the seconds not incrementing properly?

The problem may be caused by the back and forth conversion between float and int. Personally, I would use an unsigned long instead of float, with the value in 10th's of a second. Also consider that a float is only accurate to seven significant digits, limiting your time to a bit less than 278 hours.

while(millis() < time_now + period) { //non blocking delay

That's not a non blocking delay, it's just a delay made using millis and is just as blocking as the standard delay. Please read the examples of how to use millis.

Hi- the seconds on the lcd screen aren’t incrementing. They just show zero, and then when the minutes go to 1 the seconds show “0-60”. At two minutes I get “0-120”.
They are incrementing just fine on the serial display. That’s why I’m pretty sure the issue is somewhere in the functions that format the time for display

have a look at the sample code I provided in #2 as an example of a non blocking code and how to extract hour, minutes and seconds from a counter based on millis()

1 Like

Definitely will once I’m back in front of the computer (had to move to the phone for a while and reading code there is difficult). Appreciate the help with the overall program. Still wouldn’t mind seeing where the glitch is in my code just for my own benefit

I commented on some lines that I found unnecessary and made a comment on a part of your code that I didn't understand the purpose of.
It's about the last digits on the LCD.

PS; I put a value of 3599 in both time variables to facilitate debugging.

float onTimeAccumulator = 3599; // total amount of time the pump is on
float offTimeAccumulator = 3599; // total amount of time the pump is off

See my modifications here or in the simulator.

#include "Wire.h"               // Library for I2C communication
#include "LiquidCrystal_I2C.h"  // Library for LCD

LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4);  // Change to (0x27,20,4) for 20x4 LCD.
int period = 100; //time between increments- currently set to tenths of a second
float secondCounter = 0; //counts number of times the pump comes on- not currently implemented
int floatSwitchPin = 7; //which pin the float switch is connected to

boolean pumpOn = false; // State of the pump- starts as "off" (False) and switches to "on" (True when pin7 is shorted to ground)
boolean lastPumpState = false;

float onTimeAccumulator = 3599; // total amount of time the pump is on
float offTimeAccumulator = 3599; // total amount of time the pump is off


void setup() {
  //start LCD screen
  lcd.init();
  lcd.backlight();
  // Initialize sensor pin
  pinMode(floatSwitchPin, INPUT_PULLUP);
  // Open Serial monitor for debugging
  Serial.begin(9600);
}

void loop() {
  unsigned long time_now = millis();
  if (pumpOn == true) { // Check to see if the pump is on
    onTimeAccumulator = onTimeAccumulator + 0.1; //increase the onTimeAccumulator by 0.1 seconds each loop when pump is on
  }
  else { //pump is off
    offTimeAccumulator = offTimeAccumulator + 0.1; //increase the offTimeAcccumulator by 0.1 seconds each loop when pump is off
  }

  //Debugging Serial Print Statements
  printTime(0, 0, onTimeAccumulator);
  printTime(0, 1, offTimeAccumulator);
  /*  Serial.print("onTimeAccumulator=");
      Serial.print(onTimeAccumulator);
      Serial.print("  offTimeAccumulator=");
      Serial.print(offTimeAccumulator);
      Serial.print("  pumpOn=");
      Serial.println(pumpOn);
  */

  while (millis() < time_now + period) { //non blocking delay
    pumpOn = !digitalRead(floatSwitchPin);
  }
}


void printTime(int x, int y, float seconds) { //Convert Seconds into user friendly display of HH:MM:SS.T
  if (secondsToHours(seconds) > 99) {
    x = x + 1; //If Seconds is bigger than 99 move over one column
  }
  lcd.setCursor(x, y);              // Set col 0
  if (secondsToHours(seconds) < 10) { //if hours is less than 10 add a leading 0
    lcd.print("0");                 //After print move to col 1
    //lcd.setCursor(x + 1, y); //move over one column   Set col 1 (unnecessary)
  }
  lcd.print(secondsToHours(seconds)); //print number of hours (using subroutine below)
  lcd.setCursor(x + 2, y); //move over 2 columns  // Set col 2
  lcd.print(":"); //print the colon    //After print move to col 3
  //lcd.setCursor(x + 3, y); //move over 3 columns    Set col 3 (unnecessary)
  if (secondsToMinutes(seconds) - (secondsToHours(seconds) * 60) < 10) { //if number of minutes is less than 10 add a deading 0
    lcd.print("0");     //After print move to col 4
  //  lcd.setCursor(x + 4, y); //move over    Set col 4 (unnecessary)
  }
  lcd.print(secondsToMinutes(seconds) - (secondsToHours(seconds) * 60)); //print number of minutes (minus minutes accounted for as hours)
  lcd.setCursor(x + 5, y); //position cursor   Set col 5
  lcd.print(":"); //print colon //After print move to col 6
  //lcd.setCursor(x + 6, y); // position cursor  Set col 6 (unnecessary)

  // I don't understand what should happen with this part.
   // I have modify to avoid neg. numbers
// ==========================================================================
/*
  if (secondCounter - ((secondsToMinutes(seconds) * 60) + secondsToHours(seconds) * 3600) < 10) { //if number of seconds (minus seconds accounted for as minutes and as hours) is less than 10 add a leading zero
    lcd.print("0"); //add a leading zeor
    lcd.setCursor(x + 7, y); //move the cursor over
  }
  lcd.print(secondCounter - (((secondsToMinutes(seconds) * 60) + secondsToHours(seconds) * 3600)), 1); //print number of seconds (minus seconds accounted for as hours and minutes)
*/
// ==========================================================================
  float newSecondCounter = secondCounter - ((secondsToMinutes(seconds) * 60) + secondsToHours(seconds) * 3600);
  if (newSecondCounter < 0 )
    newSecondCounter = newSecondCounter * -1;
  if (newSecondCounter < 10) { //if number of seconds (minus seconds accounted for as minutes and as hours) is less than 10 add a leading zero
    lcd.print("0"); //add a leading zeor   //After print move to col 7
  //  lcd.setCursor(x + 7, y); //move the cursor over   Set col 7 (unnecessary)
  }
  lcd.setCursor(x + 6, y); //move the cursor over
  lcd.print(newSecondCounter, 1); //print number of seconds (minus seconds accounted for as hours and minutes)
 // ===========================================================================

}

int secondsToMinutes(float seconds) { //converts seconds into minutes
  int minutes;
  minutes = floor(seconds / 60);
  return (minutes);
}

int secondsToHours(float seconds) { //converts seconds into hours
  int hours;
  hours = floor(seconds / 3600);
  return (hours);
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.