System Uptime: millis to DD:HH:MM code optimization? (esp8266)

Hi guys!

I’m actually displaying my System Uptime using millis.
(It shows a value of DAYS:HOURS:MINUTES since the esp8266 NodeMCU’s boot time). I am aware the millis() function rolls over in about 47 days, and that it’s not an atomic clock, but it’s more than enough to debug my projects without using external hardware…

This is my Cayenne dashboard (MQTT)…

QUESTION:
I am making use (abuse maybe :grin: ) of the “modulo” function. It sends each value to a different Cayenne Channel. This is the code snippet…

void publishSystemUptime() {
long millisecs = millis();
systemUpTimeMn = int((millisecs / (1000 * 60)) % 60);
systemUpTimeHr = int((millisecs / (1000 * 60 * 60)) % 24);
systemUpTimeDy = int((millisecs / (1000 * 60 * 60 * 24)) % 365);

}

I’m pretty shure there IS a better way of doing this :smiley:
(BTW, it actually works just fine and it’s really stable!)

Any ideas/suggestions for optimizing the code?
Thanks for your time guys…

Cheers!
Marc

SYS UT Dashboard.png

Sys UT Code.png

I'm not sure how else you would do it. That's how the breaktime function in the Time library does it. It's an expensive calculation. But there's not really any other way to do the math.

Thanks Delta_G

I didn't know that! Would I save some CPU by explicitly defining the multiplications (for example instead of 1000 * 60, just writing 60000? Is it worth, or the compiler would do that for me?)

The compiler will handle those sorts of things for you. It’s pretty smart. I’d leave them laid out as you have to make it more readable and easier to see where the numbers come from.

Thanks!

I really appreciate your help, clear and concise..

Cheers!

I made this point where you posted this EXACT SAME QUESTION on SE but I'll make it here too just for completeness (why do I have to do it in two places?).

The 60000 won't fit into an int. So when you write it out in terms that all do fit into an int you have to force the compiler to treat them as unsigned long.

Write (1000ul * 60)

Delta,

First of all, not trying to be rude or anything. I'm just new to Arduino and was "trying out" which forum has better active community. Sorry for the inconvenience, really. My bad.

BTW
Could it be that the compiler is smart enough to correctly initialize the variables?
The sketch is actually working, for the last 2 days, 3 hours, 2 minutes and counting, and it's really quite precise too!

spacefolder:
Could it be that the compiler is smart enough to correctly initialize the variables?

It’s not. That problem is the root of 99% of the “My arduino gets simple calculations wrong” threads. If it sees (1000 * 60) it’s going to put -27233 ( or something close to that, did the math in my head)

Also posted at https://stackoverflow.com/q/45516695

spacefolder:
which forum has better active community.

For the most part it's the same people.

spacefolder:
First of all, not trying to be rude or anything. I’m just new to Arduino and was “trying out” which forum has better active community. Sorry for the inconvenience, really. My bad.

I don’t like the cross-posting to multiple sites but from the response to a couple previous posts where I complained about it, apparently a significant number of the regulars here think it’s fine so I can only post links to each post when I notice it being done.

If you’re going to cross-post then please add a link from each post to the other. Otherwise we might waste a bunch of time giving an answer or requesting clarifications that were already provided in the other post. This will also make it easier for anyone else who later has the same question to find the solution.

Hi guys, once again, I do apologize..
But this is turning into a growing snowball.
The post at SE has already been deleted a while ago.

I've used something like this to make a simple clock / uptime meter.
This might not be the exact code I used (I'm not sure what code I did use, exactly), but the basic idea is the same as what I used.

EDIT: That could not have been the same as what I really used, because it contains bugs which would prevent it from working.

// this part goes in your "setup"
byte hh=0; byte mi=0; byte ss=0; // hours, minutes, seconds
unsigned int dddd=0; // days (for if you want a calendar)
unsigned long lastTick=0; // time that the clock last "ticked"
char timestring[25]; // for output


// this part goes in your "loop"
if ((micros() - lastTick) >= 1000000UL) {
  lastTick += 1000000UL;
  ss++;
  if (ss>=60) { ss-=60; mi++; }
  if (mi>=60) { mi-=60; hh++; }
  if (hh>=24) { hh-=24; dddd++; }
}


// To display the time in the serial monitor:
sprintf(timestring,"%d days %02d:%02d:%02d", hh, mi, ss);
Serial.print(timestring);

EDIT: Use the code below, instead. I haven't tested it, so I'm still not 100% sure it will work, but it's better than the above code, which definitely won't work.

// this part goes at the beginning of your code, before "setup"
byte hh=0; byte mi=0; byte ss=0; // hours, minutes, seconds
unsigned int dddd=0; // days (for if you want a calendar)
unsigned long lastTick=0; // time that the clock last "ticked"
char timestring[25]; // for output


// this part goes in your "loop"
if ((micros() - lastTick) >= 1000000UL) {
  lastTick += 1000000UL;
  ss++;
  if (ss>=60) { ss-=60; mi++; }
  if (mi>=60) { mi-=60; hh++; }
  if (hh>=24) { hh-=24; dddd++; }
}


// To display the time in the serial monitor:
sprintf(timestring,"%d days %02d:%02d:%02d", dddd, hh, mi, ss);
Serial.print(timestring);

Hi Odometer, thanks for the reply.

It´s quite a differente approach, I´ll sure give that a try!
Although, I continued my googling, and apparently everybody is using the modulo function to perform this task.

I think (not sure though) that it is a slicker and more optimized code, that doesn't rely on so much IF comparisons. BTW, if I have some spare time this weekend I could test (count) the "milliseconds" it takes for each method, and shine a light on the subject.

People, what is your opinion?

Cheers!

It would be more efficient to increment in the time modulus than to break down a binary count modulo 60/60/60. In other words, just implement an odometer that counts in HHMMSS, updated every secode. It may look ugly but I’m sure that the compiled code would be much faster.

Here is a demo:

// Yet another blink without delay sketch
// aarg 2017 in the Arduino forum

#define BAUDRATE 600
#define NUM_STARTBITS 1
#define NUM_STOPBITS 1
#define BITSPERCHAR 8
#define CHARS_PER_SEC  (BAUDRATE / (NUM_STARTBITS + BITSPERCHAR + NUM_STOPBITS))
#define CHARS_PER_LINE 10
#define LINES_PER_SEC  (CHARS_PER_SEC / CHARS_PER_LINE)

int lineCount;

char secondL = '0';
char secondH = '0';
char minuteL = '0';
char minuteH = '0';
char hourL = '0';
char hourH = '0';

uint8_t ledState;

void setup() {
  Serial.begin(BAUDRATE);
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.println("Push <enter> key to start clock");
  Serial.println("and blink built in LED");
  while ( not Serial.available());
}

void loop()
{
  // must send same number of  characters as CHARS_PER_LINE
  Serial.write('*');
  Serial.write(hourH);
  Serial.write(hourL);
  Serial.write(':');
  Serial.write(minuteH);
  Serial.write(minuteL);
  Serial.write(':');
  Serial.write(secondH);
  Serial.write(secondL);
  Serial.write('\n');
  
  lineCount++;

  if (lineCount >= LINES_PER_SEC)
  {
    lineCount = 0;
    ++secondL;
    if (secondL > '9')
    {
      secondL = '0';
      ++secondH;
      if (secondH > '5')
      {
        secondH = '0';
        ++minuteL;
        if (minuteL > '9')
        {
          minuteL = '0';
          ++minuteH;
          if (minuteH > '5')
          {
            minuteH = '0';
            ++hourL;
            if (hourL > 9)
            {
              hourL = '0';
              ++hourH;
              if (hourH > '9')
              {
                hourH = '0';
              }
            }
          }
        }
      }
    }
    ledState = not ledState;
    digitalWrite(LED_BUILTIN, ledState);
  }
}

This demo uses a trick of incrementing ASCII characters, you can dismiss that and use integer variables for HH MM and SS. It also uses the serial port for timing, that is just a trick, you should of course use millis().