Number of events over an interval of time Question

I'm working on some logic and trying to see how best to approach it. I have a project that will schedule a number of events with in a given period of time. I'm using a NodeMCU (8266) with a 1.5" display, speaker, and two relays.

Here is the situation:

  1. I have a random time (in millis) taken from two numbers (eg: 10min/60min) We land on 30mins which I'll call TotalTime
  2. I want to take that TotalTime "30 minutes" and schedule a number of random 2 minute (max) events; which could be shorter but not less than 5000ms each. Not every two minutes do I want an event, but instead a number of events should be spread randomly over the TotalTime without overlapping each other.

I was thinking about somehow making an array of events that would take the TotalTime and fraction it up into the smaller event times, but this is where my logic is failing. Once I have the start of the events, I can do a random duration from 5 seconds to 2 minutes.

Total Time = 30minutes (in ms). I would like to also block off the first five minutes to not allow something to be scheduled (setup period) so current start time + 5 minutes could be the first event, but not necessarily. Over the interval of TotalTime, there may be one or more events with the number being somehow proportional to the TotalTime.

Hope that use case is more clear them mud. Any thoughts or logic would be hepful.

So… lemme see if I understand.

Would this work:

  • Wait five minutes.

  • Repeatedly do things one at a time, one after the other, that take between 5 seconds and 5 minutes.

  • Once those events have "used up" an initially chosen total duration, just stop.

Does anything make this rinse and repeat?

a7

How random does it need to be? You can get random values reading an ADC, but it's typically a repeatable random pattern on power up each time. Otherwise, you'd want to use another source.

I think it would be wait five minutes and then take that total block of time and try to fit in smaller events that would be a max of 2minutes. Maybe chunking the time into mini events and only select one or more of those chunks to be an actual event and the other a null event. So you may take the 30 minutes and chunk it into 2 minute (15 total events) and only choose three of the actual events to make the thing go on. It is the logic of carving up a time that could be between 30 minutes and lets say 3 hours. Finding the best way to create intervals (many a modulos function) and from these back to back events pick a few that would be active events and the other non-active. You got me thinking here... thank you.

Randomness if not the real issue, but more how to carve up time and create events that turn on an object (lets say a light). I want that light to go on for a period of time (between 5seconds and 2 minutes). The random portion is how to make the on and off start times that fit within the total time selected. Hope that make sense.

Likewise. :wink:

So… a large amount of time, carved into many intervals all the same duration, some of which will do something, real events, the rest which will not, but take the same time.

I think.

Then it seems simple enough. Choose a random block of time, say 30 minutes.

Choose the small interval, say 90 seconds.

Do 30 minutes / 90 seconds number of events, choosing at random each event period whether it is a real event or a dummy wait it out event.

Skip the fractional event that might pop out of the maths, or go over the initially chosen total duration by as much as a hole event minus a teeny bit.

As for randomness, @apf1979 mkes a valid point. I suggest to you to not worry about it until you get it working just using random(). Sometimes the fact that you will get the same sequence over and over can be an advantage during development. Worry about cryptographic quality randomness if you ever really feel the need to. Otherwise, there are a variety of cheap and entire adequate means of obtaining randomness no human will distinguish as pseudo. Random.

a7

not sure i understand completely
consider following
specify # of events, % of event and duration of event (all can be scaled)

    0   50
    1    0
    2    0
    3   33
    4   39
    5   50
    6    6
    7    0
    8   28
    9    8
   10   41
   11    4
   12    0
   13   18
   14    0
   15   40
   16    0
   17   56
   18    0
   19   33
   20    0
   21   34
   22    0
   23   40
   24    8
   25    0
   26    0
   27    6
   28    9
   29    0
const int MaxEvents = 100;
int event [MaxEvents];

char s [80];

// -----------------------------------------------------------------------------
void
process (
    int    *buf,
    int     nEvents,
    int     duration,
    float   pct )
{
    memset (buf, 0, sizeof(event));
    for (int i = 0; i <nEvents; i++)  {
        if (pct > random (100))
            buf [i] = 1 + random (duration);

        sprintf (s, " %4d %4d", i, buf [i]);
        Serial.println (s);
    }
}

void
setup ()
{
    Serial.begin (9600);
    process (event, 30, 60, 50);
}

void loop ()
{
}

@alto777 I think this is very close to what I was looking for.

  1. Take a chunk of time (30 Mins)
  2. Find how much of that is divisble by the main chuck (30 mins) such as 2 minute events
  3. Of those 15 possible events (dropping the ones that do not fit in) select a random number of them to be live events and the rest are dummy events.
  4. Complete the events and end at the 30minute mark (DONE)
  5. To restart the process reboot the device.

Now I need to figure the best way to store the events and which are live (an array maybe) and then convert those events into mills points along the 30 minute (or what ever) timeline.

@gcjr, let me have a look over your code and ideas above. I'm really looking to select a random timeline (30 - 120 minutes) that is the duration of the total process. From that, I want to randomly select different intervals within that same timeline that do not overlap. Really cut the timeline into mini 2 minute events, but some of those events will be live (trigger something) and the others will be just null events (the time runs where nothing happens. That also will be a random event dime from let's say 5 seconds to a max of 2 minutes; during this time a light or something will trigger. But I do not want this to be the same each time, so I would like to select maybe 3-6 of the events from the total list to be live. Hope that helps with what I was looking for. I may even look at drawing an image as that makes it easier for me to comprehend. :slight_smile:

isn't that a random choice? and since you said each event is no more than 2 mins, that determines the # of events: 15-60.

once you decide if an event occurs, rand < some value, within that event interval, you can then use rand() to determine the type of event and duration: 5-120 sec

OK TBC all events, real or other, in the big chunk (e.g. 30 minutes) are to be the same length (e.g. 90 seconds), those to be chosen ahead of time.

I don't see a need to store anything. March an event out regularly (90 seconds like), decide if it is live or fake on the fly, and either do or don't something for the event duration.

Even if the events are variable duration within a range, you can still do everything just in time.

Unless you've left off something about needing to know all about every detail in advance of the run, or details around lining up the real activities.

a7

@alto777 as I think about it, you are correct, I would not need to have the events and whether they are live or not stored. More an IF, or a few, statement(s) that say If this is event#1 check to see if this is a live or not event. IF it is a live event, set a time that falls within the 5 second - 2 minute max and call the function to do the thing. If the MILLIS starts Event#2 loop back through until all the events are completed. I may have a variable that would increase/decrease the frequency of a live event but off to look at the math that may help me chunk up the time and put this in a loop and function. I cannot use a delay as the system is performing other timed functions so this will need to be done using millis. I guess that was why I was first thinking of creating an array of start events in millis and while building this array also having one that hold the live/not live and event a random duration. That way I could have a loop that would walk the array and activate based on the data within the arrays. Thanks for all your assistance hashing this out. Not it is taking a concept and putting it to code. Always the fun part.

OK, if you'd like to describe what the real events consist in, and confirm that non-event events are just waiting out the time period we could perhaps make suggestions about how to

You can't just call an event function that might take a good sweet fraction of the time slot without that having an impact on the performance of the other processes you want to be lively.

Would "blink an LED N times, X milliseconds ON, Y milliseconds OFF" describe enough of an event function?

If you could make that happen one-handed, so to speak, and the other hands could continue reciting the Gettysburg Address or whatever?

Now would be a good time to become intimately familiar with the classic Arduino "blink without delay" pattern, Google it and give it all the time you need to come to grips with 'xactly how it works.

If you haven't done aready. :wink:

We can say more about all that. The concepts are universal, and the execution can show a wide variety of programming styles and need for language competence.

HTH

a7

Today I have the core systems running using only millis and that works without any issues. Let's say this core functions is to lock a door for the entire 30-120 minute time. The next phase in the project I wanted to add in were the additional "events" such as to randomly turn on a light switch. These events I want to happen during some perceived random intervals and not during others. The events to turn on the light would also be somewhat random in length from 5 seconds to 2 minutes; being 2 minutes max.

The thoughts would be:

  1. Get the total time of the door being locked (already done in my code via millis timer)
  2. Check counter to see if the time passed is > 5 minutes before starting the first possible event (lights on)
  3. Determine of the light should or should not go on during this event (true/false)
  4. if True, Get a random length of that one event from 5 seconds to 2 minutes to enable light
  5. When the event is done, rinse and repeat for the total number of events
  6. Unlock the door at ensure the light is off at the end of total time.

A few logic bits and variables I have been thinking about.

  1. StartTime (millis)
  2. TotalTime (millis) of the door to be locked
  3. DelayTime (millis) time to wait before events can happen
  4. FirstEventStart (millis) = StartTime + DelayTime
  5. TrueEvent (Boolean) Is this a lights on event or not
  6. TrueEventTime (millis) contain a random number from 5000 - 120000 (2 mins)
  7. NextEvent (millis) = PreviousEventStart + 120000
  8. Repeat?

i think your contemplating how you would do things when you aren't sure about what to do..

code is usually much easier to design once there is a clear understanding of what needs to be done.

for example

why?

@gcjr, thank you for continuing to read and provide insight to this idea. I'm happy to be redirected and here to learn.

The reason to know the length of the door being locked is the core element in this project. A locked door and enabling a light are only examples to help people better visualize things.

Right now, the current state of the project finds a random time to lock the door from 30mins to 120mins. This length of time is randomly selected and stored in a variable in the setup function. Here I also setup the milliseconds the project is running and how many to the door open (end) event. Right at the end of the setup I call locked() and this locks the door and labels the display with "LOCKED"

This loop simply checks if the door is in a locked locked state and if so, update the display with the remaining time until it is unlocked and also has the millis exceeded the total locked time. I also check to see if the time is 5 minutes before unlocking and flash the led every second. If 1 minute or less, I flash the LED every half-second and beep the speaker to let a person hear that in less than a minute the door will be unlocked.

All of the above is done today and it works great.

I'm wanting to add some additional random events that will carve up the full time the door is locked into smaller 2 minute time segments that have the potential to be a true light on or false light off. All if this is done today using millis and no delay functions after from playing an end tone when the door is unlocked.

Having this time the door is to be locked at the start, this is where I then want to break it up into 2 minute potential light on events, which each event may be a true (light on) or false (do nothing/null) for that specific 2 minute event. If the event is a true one, then I want to have a random time from 5 seconds to the whole 2 minutes to turn a light on via a relay. After the first 2 minutes, go back and check is the program has moved into the second event, and here start the process again to determine if it is a real event (light on for a random time) or a null (do nothing event). Rinse and repeat for the total number of 2 minute events that fit in the larger door locked time.

These 2 minute events would continue while the full time the door is locked. At the end of the door lock time, the door would unlock and the light would go off.

Imma suggest you post the code for the best current working version of this before any enhancements.

And I remain unclear on the specs. Little bit.

What goes into deciding an interval is for a real event or for a sit on you hands non- event?

Do I understand that the two minute fixed intervals that are real would be some time with a light on, and the remainder of the time with the light off?

TIA

a7

1 Like

i thought you had already stated this. and i posted code to divide some period of time into 2 min periods during which something may or may not randomly happen and if it does, it randomly occurs from 5 sec to 2 minutes.

did i miss something? apparently what i posted must not be correct

have you tried playing with rand() to implement what you have said?

OK, I'm assuming that posting my code will not have others criticize me for my programming prowess and solutions thus far. :slight_smile: As I said earlier, I'm here to learn and be educated. So any recommendations. Just be aware that it is not optimized but more a rough pass to prototype my project.

The full code is posted below for what is currently working. Here I have a lock timer (door example) and then you will see the start of a few variables that I was beginning labeled as relay (these would be the lights).

I know the speaker tones in the unlock function could be put in an array and loop it, i recently added just to hear a different tone when the unlocked function was called.

Note, this is all done today on an ESP8266 NodeMCU Development board with an external speaker, 1.5" OLED Display, door locking is via a 12v magnetic lock, and the future light on relay functions will be done via one or two relays.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET     -1                                     // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(128, 64, &Wire, -1);

int LED2 = 16;                                                // Assign LED1 to pin GPIO16
int SPEAKER = D7;
int MaxRandomNunber = 120;                                    // Max Random Number in Minutes
int MinRandomNumber = 30;                                     // Min Random Number in Minutes
int PWMA = D1;                                                // Lock 1 on D1
int PWMB = D2;                                                // Lock 2 on D2
int Relay1 = D3;                                              // Relay #1 on D3
int Relay2 = D4;                                              // Relay #2 on D3
unsigned long LockTimer = 60000;                              // LockTime in Milliseconds
int MinuteTimer = 10;                                         // LockTime in Minutes
unsigned long DeviceStartTime;                                // Mills value at start of locking
int LockStatus = 0;                                           // LockStatus 0= unlocked 1=locked
int Relay1Timer = 0;                                          // Relay1 Timer Set to Zero
int Relay2Timer = 0;                                          // Relay2 Timer Set to Zero
int Relay1Status = 0;                                         // Relay1 Status Set to OFF
int Relay2Status = 0;                                         // Relay2 Status Set to OFF
int MinutesRemaining = 0;                                     // Be able to flash LED when ready to unlock
int counter = 0;                                              // Loop Counter
int beeped = 0;                                               // Varible to say if the beep was done.
//
// Start Setup Loop
//
void setup() {                                                
  Wire.begin(14,12);                                          // Change I2C pins to D5 / D6
  pinMode(LED2, OUTPUT);                                      // Init Onboard LED
  pinMode(SPEAKER, OUTPUT);                                   // Init Speaker Output
  pinMode(PWMA, OUTPUT);                                      // Init Lock 1
  pinMode(PWMB, OUTPUT);                                      // Init Lock 2
  pinMode(Relay1, OUTPUT);                                    // Init Relay #1
  pinMode(Relay2, OUTPUT);                                    // Init Relay #2

  Serial.begin(9600);                                         // Enable serial logging

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);                  // Init the Display 
  display.clearDisplay();                                     // Clear the Display
  
  MinuteTimer = getRandom(MinRandomNumber, MaxRandomNunber);  // Generate Random Number based on MIN/MAX Assigned
  LockTimer = MinuteTimer * 60000;                            // Set the duration from of lock from Minutes to Milliseconds
  
  Relay1Timer = MinuteTimer;

  Serial.print("Timer Amount:");
  Serial.println(MinuteTimer);
  DeviceStartTime = millis();                                 // This is the time the device started up in Milliseconds
 
  digitalWrite(LED2, HIGH);                                   // Set Locked LED Signal OFF
  digitalWrite(Relay1, HIGH);                                 // Init Relay1 off
  digitalWrite(Relay2, HIGH);                                 // Init Relay2 off
  
  lock();                                                     // Ater all settings, Start the Lock process
}
//
// The loop function runs forever
//
void loop() {                                                 
 
  MinutesRemaining = ((LockTimer - millis())/60000+1);        // Calculate and store thre Minutes locked remaining
  
  display.clearDisplay();                                     // Clear the display in the loop
  
  if (LockStatus == 1) {                                      // If LockStatus is 1 display LOCKED message
    display.setCursor(0,0);                                   // Set Cursor to print message
    display.print("LOCKED  ");                                // Print "LOCKED" to OLED
    display.setTextSize(2);                                   // Setup Minutes Remaining Message
    display.setTextColor(WHITE);
    display.setCursor(0, 16);
    display.print("Remain: ");
    display.setCursor(82, 16);
    display.print(MinutesRemaining);
  } else  {                                                   // If LockStatus is 0 Display UNLOCKED message
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0,0);                                   // Set the cursor for the message
    display.print("UNLOCKED");                                // Print "UNLOCKED" to OLED
  }
  
  display.display(); 
  
  if ((MinutesRemaining == 1) && (LockStatus == 1)) {         // If the minutes remaining is 1 Flash the Onboard LED FAST
    FlashLED(300);
  }
  
  if ((MinutesRemaining <= 5) && (MinutesRemaining > 1) && (LockStatus == 1)) {  // If the minutes remaining is 5 Flash the Onboard LED SLOW
    FlashLED(1000);
  }
  
  if ((millis() >= LockTimer) && (LockStatus == 1)) {         // If the Lock Timer runs out AND the lock is locked, call unlock
    unLock();
  }
  
}

//
//
//
int getRandom(int min, int max) {                             // FUNCTION to generate the Random minutes between MIN/MAX values
  int x;
  x = random(MinRandomNumber,MaxRandomNunber);
  return x;
}
//
//
//
void lock() {                                                  // FUNCTION to engage the lock(s)
  digitalWrite(LED2, LOW);                                     // Onboard LED On 
  digitalWrite(PWMA, HIGH);                                    // Lock1 Locked
  digitalWrite(PWMB, HIGH);                                    // Lock2 Locked
  LockStatus = 1;                                              // Set Locked Status to TRUE (1)
  Serial.println("LOCKED");
  return;
}
//
//
//
void unLock() {                                                // FUNCTION to unlock the lock(s)
  digitalWrite(LED2, HIGH);                                    // Onboard LED Off
  digitalWrite(PWMA, LOW);                                     // Lock1 Unlocked
  digitalWrite(PWMB, LOW);                                     // Lock2 Unlocked
  LockStatus = 0;                                              // Set Locked Status to FALSE (0)
  tone(SPEAKER, 360);
  delay(300);
  tone(SPEAKER, 365);
  delay(300);
  tone(SPEAKER, 370);
  delay(300);
  tone(SPEAKER, 375);
  delay(300);
  tone(SPEAKER, 380);
  delay(300);
  tone(SPEAKER, 385);
  delay(300);
  noTone(SPEAKER);                                             // Once unlocked stop speaker tone
  return;
}
//
//
//
void FlashLED(int x) {                                         // FUNCTION to Flash Onboard LED and beep Speaker (if used)
  digitalWrite(LED2, (millis() / x ) % 2);                     // Toggle on or off the LED based on Modulus of Millis and Duration (x)
  if (MinutesRemaining <= 1) {                                 // One minute and below, warn with a beep if unable to see flash
    if (((millis() / x ) % 2) == 0) {                          
      tone(SPEAKER, 349);
    } else noTone(SPEAKER);
  }
  return;
}

I'm going to see if the text image created helps to provide more insight. As they say, a picture is worth a thousand words.

*********************************************************************************************
*              |      |      |      |      |      |      |      |      |      |      |      *
*   5 Min      |event1|event2|event3|event4|event5|event6|event7|event8|event9|event0| open *
*   Delay      |  On  |  On  | Off  |  On  |  Off |  Off |  Off |  On  |  Off | Off  | null *
*********************************************************************************************
^                     ^      ^                                                              ^
|                     |      |                                                              |
|        Events are spaced out to fit total time, smaller are null events                   |
|        Events have a total time of 2min intervals with rnd choice of on/off               |
|        Events, if on (True) rnd 30sec to 2 min (during event) relay on                    |
|                                                                                           |
|                                                                                           |
|                                                                                           |
|--------------------------------- Total Time Door Locked ----------------------------------|
                        Total Time Locked is rnd between 30-240 mins