Software timers solution

If you use Blynk, you are restricted to 16 timers, along with 10 RTOS timers in Particle enviroment. In a complex project, these timers may be exhausted quickly. One solution is to implement timers within the loop; an example follows.

void loop()
{
Blynk.run();
timer.run();

const uint64_t now = System.millis(); 

// Timer 1
static uint64_t lastTimeUpdate = 0;
if (now - lastTimeUpdate >= 1000ULL)
{
    lastTimeUpdate = now;
    if (Blynk.connected())
    {
        displayTimeOnV10();
    }
}

// Timer 2
static uint64_t lastBatteryUpdate = 0;
if (now - lastBatteryUpdate >= 5000ULL)
{
    lastBatteryUpdate = now;
    updateBatterySystem();
}

// Timer 3
static uint64_t lastTFTUpdate = 0;
if (now - lastTFTUpdate >= 250ULL)
{
    lastTFTUpdate = now;
    updateTFTBrightness();
}

}

Because you're using uint64_t with System.millis(), you've also got an absurdly long rollover horizon( compared with ordinary 32-bit millis() A one MS timer will take 584.5 million years to roll over compare to 49.7 days on a 32 bits timer. This is clean, non-blocking, and scalable approach. I am using the Photon2.

Thanks, that is useful information.

Another option is to use a finite-state machine to implement control and updates for various functions.

For example, on my Photon 2 projects I typically have one interrupt timer that runs every second. It does all basic I/O. In the Loop() I update some local time variables, which synchronize with the cloud time (if WiFi is working). Various functions use a combination of this local time and I/O information to trigger an operation and update of the corresponding finite-state machine. This allows control functions to operate when time isn't the only input, such as sending information externally at regular intervals, but only if certain error sequences have occurred. Note that this can be done with several logic variables, but it can be confusing to keep track of the present state if not using a finite-state machine method.