Function uptime()

Hello all,

I have created the uptime() function to show human readable Arduino uptime based on millis() counter.

That is the code:

char *uptime() // Function made to millis() be an optional parameter
{
  return (char *)uptime(millis()); // call original uptime function with unsigned long millis() value
}

char *uptime(unsigned long milli)
{
  static char _return[32];
  unsigned long secs=milli/1000, mins=secs/60;
  unsigned int hours=mins/60, days=hours/24;
  milli-=secs*1000;
  secs-=mins*60;
  mins-=hours*60;
  hours-=days*24;
  sprintf(_return,"Uptime %d days %2.2d:%2.2d:%2.2d.%3.3d", (byte)days, (byte)hours, (byte)mins, (byte)secs, (int)milli);
  return _return;
}

This is the complete code with test, tips and comments:

/*
  function_uptime.pde

  Description: Show uptime system based on millis();
  Author: Evandro Stein (2011-09-02)
 */

void setup() {
  Serial.begin(115200);
  Serial.println("function_uptime.pde");
  Serial.println("");
  Serial.print("Zero = ");
  Serial.println(uptime(0));
  Serial.print("End of first day = ");
  Serial.println(uptime(86400000-1));
  Serial.print("Second day 86400000 = ");
  Serial.println(uptime((unsigned long)24*60*60*1000)); // 24hours * 60min * 60sec * 1000mili = 86400000
  Serial.print("Last millis() complete day = ");
  Serial.println(uptime((unsigned long) -1 + ((byte)(4294967295/86400000)) * 86400000 )); // -1 + 49 * 86400000 = 4233599999 
  Serial.print("Absolutely MAX millis() unsigned long is 4294967295 = ");
  Serial.println(uptime(4294967295));
  Serial.println("If millis() > 4294967295 then Arduino reset millis() counter to zero!!!");
  Serial.println("");
}

unsigned long time; // var created to show uptime more close to zero milliseconds as possible

void loop() {
  if ((millis()/1000) != time) {
    time = millis()/1000;
    Serial.println(uptime(millis()));
  }
  delay(1);
}

char *uptime() // Function made to millis() be an optional parameter
{
  return (char *)uptime(millis()); // call original uptime function with unsigned long millis() value
}

char *uptime(unsigned long milli)
{
  static char _return[32];
  unsigned long secs=milli/1000, mins=secs/60;
  unsigned int hours=mins/60, days=hours/24;
  milli-=secs*1000;
  secs-=mins*60;
  mins-=hours*60;
  hours-=days*24;
  sprintf(_return,"Uptime %d days %2.2d:%2.2d:%2.2d.%3.3d", (byte)days, (byte)hours, (byte)mins, (byte)secs, (int)milli);
  return _return;
}

Suggestion are welcome! If you like or use it, please reply.

Thanks,
Evandro

The result will be:

function_uptime.pde

Zero = Uptime 0 days 00:00:00.000
End of first day = Uptime 0 days 23:59:59.999
Second day 86400000 = Uptime 1 days 00:00:00.000
Last millis() complete day = Uptime 48 days 23:59:59.999
Absolutely MAX millis() unsigned long is 4294967295 = Uptime 49 days 17:02:47.295
If millis() > 4294967295 then Arduino reset millis() counter to zero!!!

Uptime 0 days 00:00:01.001
Uptime 0 days 00:00:02.000
Uptime 0 days 00:00:03.000
Uptime 0 days 00:00:04.000
Uptime 0 days 00:00:05.000
Uptime 0 days 00:00:06.000
Uptime 0 days 00:00:07.001
Uptime 0 days 00:00:08.000
Uptime 0 days 00:00:09.001
Uptime 0 days 00:00:10.000

...

Absolutely useful! I see two typos in your code
#1: the word function
#2: the word first

Furthermore you don't need mills in the code, you can reuse the param x for that, 4 bytes gained :slight_smile:

char *uptime(unsigned long x)
{
char _return[32];

Best make that a "static".

robtillaart:
Absolutely useful! I see two typos in your code
#1: the word function
#2: the word first

Furthermore you don't need mills in the code, you can reuse the param x for that, 4 bytes gained :slight_smile:

Resolved! Thanks for reply.

AWOL:

char *uptime(unsigned long x)
{
char _return[32];

Best make that a "static".

Both? Like that?

static char *uptime(unsigned long milli)
{
static char _return[32];

Ok, but why?

Thanks

The function doesn't need to be static (think about it!), but the buffer does.
What happens immediately after this: return _return;happens?

AWOL:
The function doesn't need to be static (think about it!), but the buffer does.
What happens immediately after this: return _return;happens?

Hummm, I found it. We must use static to var be internal use only in a function, right?

Many thanks.

No, we use static so that the memory doesn't go out of scope when the function returns.

You need to make _return static or it will be corrupted by future function calls.

If you call uptime() and call another function (e.g. serial.Print()) before using the returned string it may be corrupted by that function.
In fact an interrupt occurring between the call to uptime() and the use of the returned string could also corrupt the result.

Iain

sixeyes:
In fact an interrupt occurring between the call to uptime() and the use of the returned string could also corrupt the result.

Great, very didactic! Thanks a lot.

In fact an interrupt occurring between the call to uptime() and the use of the returned string could also corrupt the result.

Of course, even if it is static, something like this:

char* timeZero = uptime (millis ());
... time passes
char* timeOne = uptime (millis());
... time passes
Serial.print (timeZero);

isn't going to work either!

AWOL:
Of course, even if it is static, something like this:

char* timeZero = uptime (millis ());

... time passes
char* timeOne = uptime (millis());
... time passes
Serial.print (timeZero);


isn't going to work either!

This kind of function I usually don't store the result in string, I only store the number is and call function to show same result again.
But when I want anyway store it in a array, I do it like that:

char timeZero[32],timeOne[32];
sprintf(timeZero,"%s",uptime(123));
sprintf(timeZero,"%s",uptime(321));
Serial.println (timeZero);
Serial.println (timeOne);

My quetion is: Return a pointer is not a good way to return the text in this case?

Thanks again.

But when I want anyway store it in a array, I do it like that:
Code:

char timeZero[32],timeOne[32];

sprintf(timeZero,"%s",uptime(123));
sprintf(timeZero,"%s",uptime(321));
Serial.println (timeZero);
Serial.println (timeOne);



My quetion is: Return a pointer is not a good way to return the text in this case?

Did you mean to initialise timeZero twice and not initialise timeOne?

BTW why don't you use strcpy(timeZero, uptime(123)); instead of sprintf?

Iain

My quetion is: Return a pointer is not a good way to return the text in this case?

It's a fine way to return the character data. The issue is that the pointer points to a block of memory that is not going to move. When the data in that memory location changes, every pointer that you have that points to that block of memory will, when dereferenced, see the latest data at that location, not the data that was there when the pointer was stored.

If you want to save the data that is in the pointed-to location when the function ends, you need to make a copy of it, in another location.