OK, here's what I have now.
// how_long_running.ino
// by odometer 2018-12-14
// This sketch answers the question: "How long have I been running?"
// The answer is shown on a 16x2 LCD character display.
#include <LiquidCrystal.h>
// declare and initialize variables
int nowDays = 0; // "days" part of elapsed time
int nowHours = 0; // "hours" part of elapsed time
int nowMinutes = 0; // "minutes" part of elapsed time
int nowSeconds = 0; // "seconds" part of elapsed time
int nowTenths = 0; // "tenths of seconds" part of elapsed time
unsigned long microsAtLastTenth = 0UL; // value of micros() at most recent 1/10 second
// a pretty important constant
const unsigned long MICROS_PER_TENTH = 100000UL; // number of microseconds per 1/10 second
// here we specify what pins our LCD is on
// RS EN D4 D5 D6 D7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// start the LCD going
lcd.begin(16, 2);
// display the time (which at this point will be all zeros)
updateTimeDisplay();
}
void loop() {
// check if it is time for the clock to advance
if ((micros() - microsAtLastTenth) >= MICROS_PER_TENTH) {
// make the clock advance 1/10 of a second
nowTenths++;
// make sure that the next advance happens exactly when it is due
// (they should happen exactly at intervals of 1/10 of a second)
microsAtLastTenth += MICROS_PER_TENTH;
// our clock needs to do more than count tenths of seconds
// it also needs to count whole seconds, and minutes, and hours, and days
// so let's go ahead and do that
// too many tenths?
if (nowTenths >= 10) {
// trade 10 tenths for 1 second
nowTenths -= 10;
nowSeconds++;
}
// too many seconds?
if (nowSeconds >= 60) {
// trade 60 seconds for 1 minute
nowSeconds -= 60;
nowMinutes++;
}
// too many minutes?
if (nowMinutes >= 60) {
// trade 60 minutes for 1 hour
nowMinutes -= 60;
nowHours++;
}
// too many hours?
if (nowHours >= 24) {
// trade 24 hours for 1 day
nowHours -= 24;
nowDays++;
}
// update the display so we can see the new time
updateTimeDisplay();
}
}
void updateTimeDisplay () {
// function to update the display to show the current elapsed time
//
// we want the display to look like this
// Position: 01234567890123456
// Line 0: days h m s
// Line 1: 0 00:00:00.0
// declare a buffer for storing a string (we'll need it later)
// we expect to need only 16 characters, but to be safe, let's make room for 20
// so we allow 20 characters worth of room, plus 1 extra for the null terminator
// (Always allow 1 character extra worth of room for the null terminator!)
char buf[21];
// move to the top line of the display
lcd.setCursor(0,0);
// show the units
lcd.print(" days h m s ");
// convert the elapsed time to a string
// for days, allow 5 digits worth of room
// for hours, minutes, and seconds, allow 2 digits for each, and use leading zeros
// for tenths, allow 1 digit worth of room
sprintf(buf, "%5d %02d:%02d:%02d.%1d", nowDays, nowHours, nowMinutes, nowSeconds, nowTenths);
// move to the bottom line of the display
lcd.setCursor(0,1);
// show the string for the elapsed time
lcd.print(buf);
}
And I think I'm about done with this stage. And like I said, the int for seconds, etc., would be helpful in case I end up needing to subtract time. (Want a countdown? Just change nowTenths++ to nowTenths-- and write conditionals to catch underflow!)
Personally, I prefer something like this
nowSeconds++;
if (nowSeconds >= 60) {
nowSeconds -= 60;
nowMinutes++;
}
to something like this
if (nowSeconds < 59) {
nowSeconds++;
}
else {
nowSeconds = 0;
nowMinutes++;
}
because, with the former, it is easier to see why the conditional is the way it is: it works exactly like pen-and-paper arithmetic! If I modify my code to do a countdown, I expect I will use pen-and-paper-style "borrowing", as well.