I haven't flown over your code, THX for posting it.
Your additional graph risks confusing things, as nice as it looks.
First, there is nothing complicated about cutting a large interval into smaller, identical length, intervals. Just forget about it...
All you have to do is put out the two minute events until you are at the point of having the next one run over your end limit, or what the hell, let one run over the end limit and then stop.
Later: I've missed my ride to the beach, she who must not be kept waiting would not. Wait for me to finish a little sketch. So now I get to ride my bicycle to the beach, and hope she will allow it to be tossed in the back of her vehicle for getting me home. (More up than down hill.)
I found an opening in your sketch posted in #19 where an independent event handler can be inserted.
Where you call display.display() is where I initiate or advance an event. I use a finite state machine to select timing and march out the event steps.
I could not build your sketch, so I stripped it of everything, including its pride, and ran it with the serial monitor.
All it is meant to do is show how an opportunity to advance a process can be inserted into the loop to more or less handle some other task. Watch it here, drag up the serial monitor window to see more lines of the output:
Event FSM Demo!
Because life is too short, I arranged for faster minutes and used shorter periods all over the place. Figure that out.
The only tricky trick is that the FSM is designed to be started externally, and it reports about whether it is finished or not:
if (!eventHandler(0)) // servic kick some life into it.
eventHandler(1); //
If calling the FSM for advancing the event progress says the machine is not doing an event just now, a second call immediately launches the next event.
The eventHandler() always takes the full event period. Sometimes it turns ON something for some part of that.
Run the sim and marvel just watch it sequencing. Then read the code. Then read the code. The event FSM is the last function. I larded it vigorously with print statements - I like to see why what is happening when.
// https://wokwi.com/projects/361178687373989889
// https://forum.arduino.cc/t/number-of-events-over-an-interval-of-time-question/1110643
const unsigned long ltsMinute = 30000; // milliseconds per "life too short" minute
int LED2 = 6;
int SPEAKER = 7;
int PWMA = 2;
int PWMB = 3;
const int MaxRandomNunber = 120; // minutes
const int MinRandomNumber = 30; // minutes
const unsigned int shortLife = 4000; // milliseconds
const unsigned int longLife = 7000; // milliseconds
const unsigned long intervalPeriod = 10000; // milliseconds (> short and long lifes)
unsigned long LockTimer; // LockTime in Milliseconds
int MinuteTimer; // LockTime in Minutes
bool lockedQ = false; // we locked? or not.
// Relay2 Status Set to OFF
int MinutesRemaining = 0;
void setup() {
Serial.begin(115200); // Enable 21st century serial logging
// MinuteTimer = getRandom(MinRandomNumber, MaxRandomNunber); // Generate Random Number based on MIN/MAX Assigned
MinuteTimer = 8; // just testing ma'am.
LockTimer = MinuteTimer * ltsMinute; // Set the duration from of lock from Minutes to Milliseconds
Serial.print("Timer Amount:");
Serial.println(MinuteTimer);
digitalWrite(LED2, HIGH); // Set Locked LED Signal OFF
lock(); // Ater all settings, Start the Lock process
}
void loop() {
// delay(222); // just for now
MinutesRemaining = ((LockTimer - millis())/ltsMinute+1); // Calculate and store thre Minutes locked remaining
if (lockedQ) {
static int reported;
if (MinutesRemaining != reported) {
reported = MinutesRemaining;
Serial.print("LOCKED, wait ");
Serial.println(MinutesRemaining);
}
}
if (!eventHandler(0)) // servic kick some life into it.
eventHandler(1); //
if ((MinutesRemaining == 1) && lockedQ)
FlashLED(300);
if ((MinutesRemaining <= 5) && (MinutesRemaining > 1) && lockedQ)
FlashLED(1000);
if ((millis() >= LockTimer) && lockedQ) {
unLock();
}
}
void lock() { // FUNCTION to engage the lock(s)
lockedQ = true; // Set Locked Status to TRUE (1)
Serial.println("LOCKED it ");
return;
}
void unLock() { // FUNCTION to unlock the lock(s)
lockedQ = false; // Set Locked Status to FALSE (0)
Serial.println("UNLOCKED it");
return;
}
void FlashLED(int x) {
digitalWrite(LED2, (millis() / x ) % 2);
if (MinutesRemaining <= 1) {
if (((millis() / x ) % 2) == 0) {
tone(SPEAKER, 349);
} else noTone(SPEAKER);
}
// flash reporting
unsigned long now = millis();
static unsigned long lastReport;
if (now - lastReport > 1000) {
lastReport = now;
Serial.print(" yeah, LED is flashing ");
Serial.print(x);
if (MinutesRemaining <= 1)
Serial.print(" and BEEPING");
Serial.println();
}
}
enum {IDLE, StART, ACTIVE, PASSIVE, WAITING,};
// call with true to launch an event.
// call with false to move an event along its timeline
// returns true if the machine is busy with a previously launched event
unsigned char eventHandler(unsigned char command)
{
static unsigned char state;
static unsigned long eventStartTime;
static unsigned int eventLivePeriod;
unsigned long now = millis();
// newly event?
if (command) {
state = StART;
return 1; // we busy
}
switch (state) {
case IDLE :
break;
case StART :
eventStartTime = now;
eventLivePeriod = random(shortLife, longLife);
state = random(1000) > 500 ? ACTIVE : PASSIVE;
Serial.print(millis());
Serial.print(" launching a ");
Serial.print(state == ACTIVE ? "ACTIVE" : "PASSIVE");
Serial.print(" event ");
if (state == ACTIVE) {
Serial.print(eventLivePeriod / 1000);
Serial.print(" seconds ON");
}
Serial.println();
break;
case ACTIVE :
if (now - eventStartTime > eventLivePeriod) {
Serial.print(millis());
Serial.println(" done with active period of the active event.");
state = WAITING;
}
break;
case PASSIVE :
if (now - eventStartTime > intervalPeriod) {
Serial.print(millis());
Serial.println(" done with passive event.");
state = IDLE;
}
break;
case WAITING :
if (now - eventStartTime > intervalPeriod) {
Serial.print(millis());
Serial.println(" all done with active event.");
state = IDLE;
}
break;
}
return state != IDLE; // we busy?
}
HTH and L8R
a7