Displaying time with a decimal point can it be done?

Hi all. I am fairly new to the coding scene and am working on a sketch to display run time of a compressor motor. I have created the sketch and it works ok. I am using the millis function to count the time, i can convert the milliseconds to hours no problem but the issue is im using hours as a int (id like to stay away from floats) and i want the hours to display with one place after the decimal. So basically every 6 minutes (360seconds) i want the counter to increase by 0.1. The best i can get the code to work is to have it display 1 hour every 3600 seconds.

Is there anyway i can do this? or do i have to use float type?

you can scale up your hours by factor 10

so 3 hours would be 30 and then displaying 3.0

1451.8 hours would be 14518 and a display-function could create the charactersequence "1451.8"

best regards Stefan

Thank you for your reply. Thats actually how i wanted to do it. So how would i go about displaying 30 as 3.0? i try to divide by ten but that dont work?

i try to divide by ten but that dont work?

Post what you tried

@OP

The following codes may be helpful to you.

const long interval = 200L;//200UL;//11250UL;//45000;//90000UL;//180000UL;//360000UL; (test value)
unsigned long previousMillis;
byte hour;    //hour = 00
byte myHr;
byte hrArray[3];

void setup()
{
  Serial.begin(9600);
  previousMillis = millis();
}

void loop()
{
  if (millis() - previousMillis >= interval) //for 6 min chane interval to 3600000UL
  {
    previousMillis = millis();
    hour = hour + 1;   //6 minute is added as 0.1 
    myHr = hour;
    for (int i = 0; i < 3; i++)//getting BCD values for the digits x, y, z of xy.z
    {
      hrArray[i] = myHr % 10;
      myHr = myHr / 10;
    }
    if (((hrArray[2] << 4) + hrArray[1]) == 0x24)//checking 24.0 hrs before print time and reset
    {
      hrArray[2] = 0x00;
      hrArray[1] = 0x00;
      hrArray[0] = 0x00;
      hour = 0x00;
    }
    //----------------------------------------
    if (hrArray[2] != 0)
    {
      Serial.print(hrArray[2], HEX);
      Serial.print(hrArray[1], HEX);
      Serial.print('.');
      Serial.println(hrArray[0], HEX);
    }
    else    //suppressing one leading 0 
    {
      Serial.print(hrArray[1], HEX);
      Serial.print('.');
      Serial.println(hrArray[0], HEX);
    }
  }
}

SmhrF.png

SmhrF.png

unsigned deciHours = 112;  // 11.2 hours (holds up to 6553.5 hours. Use 'unsigned long' for longer times.)

  Serial.print(deciHours / 10u);  // Print the full hours part
  Serial.print('.'); // Print the decimal point
  Serial.println(deciHours % 10);  // Print the tenths digit

@johnwasser

Cannot follow exactly!

If full codes could be made available, then an effort could be employed to understand the program logic.

GolamMostafa:
@johnwasser

Cannot follow exactly!

If full codes could be made available, then an effort could be employed to understand the program logic.

OK:

unsigned deciHours = 112;  // 11.2 hours (holds up to 6553.5 hours. Use 'unsigned long' for longer times.)


void setup()
{
  Serial.begin(115200);


  Serial.print(deciHours / 10u);  // Print the full hours part
  Serial.print('.'); // Print the decimal point
  Serial.println(deciHours % 10);  // Print the tenths digit
}


void loop() {}

[edit]While making this reply my PC decided to be stubborn. So I already made a MCVE of johnwasser's code before he did...[/edit]

The logic of @johnwasser is a heck easier to understand than all the HEX nonsense.... Although your method is basically fine, the implementation is wayyyy overcomplicated.

Stripped down version:

const unsigned long interval = 200;//11250UL;//45000;//90000UL;//180000UL;//360000UL; (test value)
unsigned long previousMillis;
byte hour;    //hour = 00

void setup()
{
  Serial.begin(115200);
  previousMillis = millis();
}

void loop()
{
  if (millis() - previousMillis >= interval) //for 6 min change interval to 3600000UL
  {
    previousMillis = millis();
    hour = hour + 1;   //6 minute is added as 0.1

    //checking 24.00 hrs and reset
    if(hour == 240){
      hour = 0;
    }
    
    byte myHr = hour;
    byte hrArray[3];
    for (int i = 0; i < 3; i++)//getting BCD values for the digits x, y, z of xy.z
    {
      hrArray[i] = myHr % 10;
      myHr = myHr / 10;
    }
    
    if (hrArray[2] != 0)
    {
      Serial.print(hrArray[2]);
    }
    Serial.print(hrArray[1]);
    Serial.print('.');
    Serial.println(hrArray[0]);
  }
}

MVCE of johnwasser's version

const unsigned long interval = 200;//11250UL;//45000;//90000UL;//180000UL;//360000UL; (test value)
unsigned long previousMillis;
unsigned deciHours = 112;  // 11.2 hours (holds up to 6553.5 hours. Use 'unsigned long' for longer times.)

void setup()
{
  Serial.begin(115200);
  previousMillis = millis();
}

void loop()
{
  if (millis() - previousMillis >= interval) //for 6 min change interval to 3600000UL
  {
    previousMillis = millis();
    deciHours++;   //6 minute is added as 0.1

    Serial.print(deciHours / 10u);  // Print the full hours part
    Serial.print('.'); // Print the decimal point
    Serial.println(deciHours % 10);  // Print the tenths digit
  }
}

And my implementation (which makes adding more decimals easy):

const unsigned long interval = 200;//11250UL;//45000;//90000UL;//180000UL;//360000UL; (test value)
unsigned long previousMillis;
unsigned deciHour = 112;  // 11.2 hours (holds up to 6553.5 hours. Use 'unsigned long' for longer times.)

void setup()
{
  Serial.begin(115200);
  previousMillis = millis();
}

void loop()
{
  if (millis() - previousMillis >= interval) //for 6 min change interval to 3600000UL
  {
    previousMillis = millis();
    deciHour++;   //6 minute is added as 0.1
    
    char hourStr[8];
    itoa(deciHour, hourStr, 10);
    
    byte n = strlen(hourStr) + 1;
    for(byte i = 0; i <= 1; i++){
      n--;
      hourStr[n + 1] = hourStr[n];
    }
    hourStr[n] = '.';

    Serial.println(hourStr);
  }
}

So multiple ways to fix it

@septillion

The logic of @johnwasser is a heck easier to understand than all the HEX nonsense.... Although your method is basically fine, the implementation is wayyyy overcomplicated.

This is the output for the codes of @johnwasser that you have supplied (Is it working!?).
SMJn.png

This is the output for the codes of @septillion that you have supplied (Is it working!?)
SMs.png

SMJn.png

SMs.png

const long interval = 200UL; What's the 'U' for?

@GolamMostafa I see perfectly working code :slight_smile:

Hint, it was you who made the implicit requirement for reset @ 24h :wink: Something you probably don't want if we talk run time of a compressor. And if you want a reset, that's the easy part as I showed in the simplified version of your method.

septillion:
@GolamMostafa I see perfectly working code :slight_smile:

Hint, it was you who made the implicit requirement for reset @ 24h :wink: Something you probably don't want if we talk run time of a compressor. And if you want a reset, that's the easy part as I showed in the simplified version of your method.

Fine! If the @OP wants to keep registering the accumulated run time, then codes both of @septillion and @johnwasser are working alright.

TheMemberFormerlyKnownAsAWOL:
const long interval = 200UL; What's the 'U' for?

Either U (for unsigned) should be removed or the modifier "unsigned" should go before the data type long. I recognize my mistake. I have edited my post.

Thank you very much.

Thanks all. I have come up with the following code

/* This code will need to run oncce an input is detected 
   for now it will be a button. The sketch will use the mills 
   fuction once it reaches 6000 it will need to add 1  to the counter. The mills will then need to reset. 
 */#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27,16,2); //lcd address
 
 
 const int swtch = 2;
unsigned long runTime = 0; 
int eeAddress = 0;
unsigned long aHours ; 
unsigned hours;
  

 




void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(swtch, INPUT);
  lcd.init(); 
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Hours");
  aHours = EEPROM.get(eeAddress, hours); 
  
  
 

}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentTime= millis();
  static unsigned long pressStartTime = 0;
int buttonState = digitalRead(swtch);
static int lastButtonState = LOW;


 if (buttonState != lastButtonState){
  // The button state has changed
  if (buttonState == HIGH){
    //button freshly pressed
    pressStartTime = currentTime; 
    

  }
  else // button freshly released
  {runTime += (currentTime-pressStartTime);
  }
  lastButtonState = buttonState; 
  Serial.println(runTime);
 }
unsigned long elapsedTime = runTime;
if (buttonState == HIGH) //counting
{
  elapsedTime += (currentTime - pressStartTime); 
  }
 
 
  // display run time

hours = (elapsedTime/1000) + aHours;

Serial.println(hours);
lcd.setCursor(7,0);
lcd.print(hours);
  //display elapsed time in hours
  EEPROM.put(eeAddress,hours); 
 
 
 

  
 

  
}

In line 72 "hours = (elapsedTime/1000) + aHours" the hours value increments by 1 for one second. ( i am just using one second now to make testing the code faster this will eventually be 360000 for the hour value to increase every six minutes). What i want to happen is the hour value to increase by 0.1 each time instead of increasing by 1. I also want the hour value to write to the EEPROM every time the hour value changes. Do i have this done correctly in the code or do the way i have it done update the EEPROM each time through the loop?
Thanks for all the help

@ryan_brown17 As @StefanL38 suggested in reply #1 and we (the rest) all agreed upon is that it's easier to keep track of deci-hours (aka, 1/10th of an hour) and just insert a decimal point when needed.

Is it with a reason you keep 'elapsedTime' and 'runTime'? Aka, do you want to display both total runtime and current runtime?

And yeah, by using EEPROM.put() the value is only written when it changed.

Just scanned your code and cleaned it up a bit. Made no real changes, just a couple of small once but mainly made it more readable.

/* This code will need to run oncce an input is detected
   for now it will be a button. The sketch will use the mills
   fuction once it reaches 6000 it will need to add 1  to the counter. The mills will then need to reset.
 */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27,16,2); //lcd address
 
const byte SwitchPin = 2;
const int EeAddress = 0;

unsigned long runTime = 0;
unsigned long aHours ;
unsigned long hours;
  
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(SwitchPin, INPUT);
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Hours");
  aHours = EEPROM.get(EeAddress, hours);
}

void loop() {
  static bool lastButtonState = LOW;
  static unsigned long pressStartTime = 0;
  
  const unsigned long CurrentTime = millis();
  const bool ButtonState = digitalRead(SwitchPin);
  
  // The button state has changed
  if (ButtonState != lastButtonState) {
    //button freshly pressed
    if (ButtonState == HIGH){
      pressStartTime = CurrentTime;
    }
    // button freshly released
    else {
      runTime += (CurrentTime - pressStartTime);
    }
  lastButtonState = ButtonState;
  Serial.println(runTime);
  }

  unsigned long elapsedTime = runTime;
  
  //counting
  if (ButtonState == HIGH) {
    elapsedTime += (CurrentTime - pressStartTime);
  }
 
  // display run time in hours
  hours = (elapsedTime/1000) + aHours;

  Serial.println(hours);
  lcd.setCursor(7,0);
  lcd.print(hours);
  
  EEPROM.put(EeAddress, hours);
}

My quick go at it:

/* This code will need to run oncce an input is detected
   for now it will be a button. The sketch will use the mills
   fuction once it reaches 6000 it will need to add 1  to the counter. The mills will then need to reset.
 */
//#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

#define lcd_setCursor(x,y) Serial.print("Cursor: ");Serial.print(x);Serial.print(", ");Serial.println(y)
#define lcd_print(x) Serial.print("Print: ");Serial.println(x)
#define lcd_init() Serial.println("lcd Init")
#define lcd_backlight() Serial.println("Backlight")


//LiquidCrystal_I2C lcd(0x27,16,2); //lcd address
 
const byte SwitchPin = 2;
const int EeAddress = 0;
const unsigned long DeciHourInterval = 1000; //360000 for 6 minutes

unsigned long totalRunTime;  //in deciHour
unsigned long currentRunTime; //in deciHour
  
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(SwitchPin, INPUT_PULLUP);
  
  lcd_init();
  lcd_backlight();
  lcd_setCursor(0,0);
  lcd_print("Total");
  lcd_setCursor(0,0);
  lcd_print("Current");
  
  EEPROM.get(EeAddress, totalRunTime);
  
  //reset if EEPROM is fresh or we are at our limits
  if(totalRunTime >= 42949672){
    totalRunTime = 0;
  }
  
  //make it deciHoud
  totalRunTime *= 10;
  
  printStates(false);
}

void loop() {
  static bool lastSwitchState = LOW;
  static unsigned long switchMillis = 0;
  
  const unsigned long CurrentMillis = millis();
  const bool SwitchState = digitalRead(SwitchPin);
  
  // The switch state has changed
  if (SwitchState != lastSwitchState) {
    //switch freshly turned on
    if (SwitchState == HIGH){
      switchMillis = CurrentMillis;
      currentRunTime = 0;
    }
    else{
      printStates(false);
    }
    lastSwitchState = SwitchState;
  }
  
  if ((SwitchState == HIGH) && (CurrentMillis - switchMillis >= DeciHourInterval)) {
    switchMillis = CurrentMillis;

    //increment timers
    totalRunTime++;
    currentRunTime++;

    //and print it
    printStates(true);

    //update EEPROM is needed
    EEPROM.put(EeAddress, totalRunTime / 10);

    Serial.println();
  } 
}

/*
char* getHourStr2(unsigned long h){
  static char hourStr[12];
  
  itoa(h, hourStr, 10);
 
  byte n = strlen(h) + 1;
  for(byte i = 0; i <= 1; i++){
    n--;
    hourStr[n + 1] = hourStr[n];
  }
  hourStr[n] = 'h';
  
  return hourStr;
}*/

char* getHourStr(unsigned long h){
  static char hourStr[12];

  itoa(h / 10, hourStr, 10);
  byte n = strlen(hourStr);
  hourStr[n] = 'h';
  itoa(h % 10, hourStr + n + 1, 10);

  return hourStr;
}

void printStates(bool on){
  lcd_setCursor(8,0);
  lcd_print(getHourStr(totalRunTime));
  lcd_setCursor(8,1);
  lcd_print("        "); //be sure it's clear
  lcd_setCursor(8,1);
  if(on){
    lcd_print(getHourStr(currentRunTime));
  }
  else{
    lcd_print("Off");
  }
    
  
}

Did test it with Serial only, so hope stuff is set right for lcd now :slight_smile:

Thanks for your help. I will try this code in the morning and let you know if it works with the lcd.

septillion:
And yeah, by using EEPROM.put() the value is only written when it changed.

@Septillion. With regards to the following line of code

 if ((SwitchState == HIGH) && (CurrentMillis - switchMillis >= DeciHourInterval)) {[color=#222222][/color]
    switchMillis = CurrentMillis;

The issue im having is that when i press the button and the counters are counting up everything works as it should, but when i release the button for a period of time and press the button again it immediately counts up, im assuming that is because as soon as i press the button again the difference between the current millis and switchmillis is greater then 6000.