Events, interrupts, and queuing with Arduino

I have done a lot of research but would appreciate some opinions on general approaches. I'm working on a program to do the following:

  1. Read a digital input via interrupts (events occurring up to 50 Hz)
  2. Perform an action with a run-time variable time delay
    2a. Action consists of
  3. commanding an output ON
  4. waiting for user specified time
  5. commanding output OFF

Overall the logic is simple, however the issue I'm having is in the physical system, the next trigger event occurs before the first action is initiated. I think I need some type of event management or queuing system, but perhaps I'm overthinking it. Essentially I'm trying to accomplish a variable phase delay from trigger to action without impacting the ability to read the interrupts.

wonder1001:
Read a digital input via interrupts (events occurring up to 50 Hz)

Interrupts don't read digital inputs, so that sentence does not really make sense.

wonder1001:
the next trigger event occurs before the first action is initiated.

In other words, you get more than one trigger for the action, is it perhaps a bouncing button?

You can safely ignore such triggers, the action will be started anyway.

If you receive a trigger while the action is running, you have to decide whether the trigger should restart the action interval prolonging the action.

wonder1001:
Essentially I'm trying to accomplish a variable phase delay from trigger to action

A delay between trigger and action was not in your specification, could you make up your mind what you really want?

wonder1001:
I think I need some type of event management or queuing system, but perhaps I'm overthinking it.

I would use a state machine.

The Arduino millis() and micros() functions return 32-bit unsigned values for ms or us (to the nearest 4 us) since startup/last-rollover.

The unsigned values and math mean that end time minus start time will always give the time elapsed up to the maximum over 4 billion ms or us that 32-bits can count, -1. With millis timing that's 49.7-some days. With micros timing it's a bit over 70 minutes, IIRC.

You can save a start time, know the desired interval and check if the interval is over with

// repeat the timer, like for blinking a led
if ( millis() - start >= interval ) // if true, the interval has passed
{
// do the timed thing

start += interval; // if you want to repeat the delay on time even if the time check was a bit late
}

// one-shot timer. it takes other code to start this
if ( interval > 0 )
{
if ( millis() - start >= interval ) // if true, the interval has passed
{
// do the timed thing

interval = 0; // some other code has to set interval > 0 to get the one-shot to run
}
}

This needs no interrupts and can run many-many of these soft-timers consecutively as long as they all have their own start times.

wonder1001:
Overall the logic is simple, however the issue I'm having is in the physical system,

It will be much easier to offer relevant advice if you post your program and describe the project you are making.

Asking questions in a vacuum is a sure path to confusion - everybody having a different concept of the problem and the solution.

...R

Thanks for the help thus far. The millis() suggestion is a good one, I was just starting to investigate that path.

The physical system is a conveyor carrying objects that I need to take photos of for quality control. A first sensor identifies potential defects and generates a digital output, which I will read into the Arduino via interrupts. The conveyor speed may vary (which I will account for separately). Since the camera is farther downstream than the sensor, multiple defective objects may be on the conveyor prior to the camera being activated for the first object. This is leading me towards establishing a timestamp for each event, which I've implemented using a sub-routine each time the interrupt is driven low.

The part I haven't figured out yet is how to timestamp the subsequent objects without impacting the timestamp of the first object.

Do you have a separate sensor to tell you when an object is actually in front of the camera? And will the same Arduino take the picture as identified the defect?

I really don't understand the reason and use of a "time stamp" when a sequential count of the objects would work so much easier.

Paul

The part I haven't figured out yet is how to timestamp the subsequent objects without impacting the timestamp of the first object.

Simply use an array of timestamps, one per object.

Unfortunately there's not a separate camera at the sensor. The same Arduino that reads the first sensor will generate the trigger for the camera. Since the conveyor speed is variable, I think it becomes necessary to timestamp each event.

I'll look into arrays, this sounds like it might be the fix.

wonder1001:
Unfortunately there's not a separate camera at the sensor. The same Arduino that reads the first sensor will generate the trigger for the camera. Since the conveyor speed is variable, I think it becomes necessary to timestamp each event.

I'll look into arrays, this sounds like it might be the fix.

I don't think so, at all," I think it becomes necessary to timestamp each event.".

All you need is a sensor to tell the Arduino an object is in front of the camera position. Count the objects passing the camera position. If the camera object number matches one in your list that needs a picture, take the picture.

If the camera object number does not match any needing a picture, don't take it.

Keep a list of object numbers needing a picture. Should not need many if the camera is near the first sensor. Then each time you take a picture eliminate that count from the list.

Paul

Thanks Paul, I hadn't considered the approach of adding a second sensor. Unfortunately that part is out of my control.

wonder1001:
The conveyor speed may vary (which I will account for separately).

Does the Arduino have access to the conveyor timing, if any exists?

Can you mark the belt with numbers, letters or bar code or just a registration mark every so far and have objects placed one per marked section? You can then associate object to belt section. You might want marks readable by humans and others readable by machine.

To read bars on a moving belt would take some basic DIY level hardware and code, nothing fancy.

Timing could get a bit complicated if the speed varies unpredictably.

I do have the advantage of knowing the belt speed and plan to compensate the delay using this parameter. Below is a first attempt at using arrays to log the time for each object and respond for each event. Let me know your thoughts.

  const byte interruptPin = 2;
  int waitTime;
  int waitPin = A0;
  unsigned long objTime[30];
  unsigned long objCount = 0;
  int loopIndex = 1;
  
 
void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), camFlag, FALLING);
  }
 
 
void loop() {
    waitTime = analogRead(waitPin);       // Read potentiometer to set wait delay before sending trigger command
    //TBD                                 // Add mapping code here to map voltage to time.  This will vary based upon belt speed.
    if (objCount == loopIndex)
      { // perform action
        if ( millis() - objTime[loopIndex] >= waitTime)  //
        {
        camcycle();
        loopIndex = loopIndex + 1;
        }
        else
        {
        }
      }
      else
      {                               // skip action
      }
}
 
void camFlag(){
  if(objCount > 29)   
      {
      objCount = 1;                   // Reset buffer if objCount exceeds 29 objects
      loopIndex =1; 
      }
      else
      {
      objCount = objCount + 1;
      }
  objTime[objCount] == millis();      //store time of event in array per each object
}
 
void camcycle(){    //action to take when interval trigger met
// TBD:  insert code here to generate trigger pulse
}

If you predict the timing based on one speed and the speed varies before the predicted event, you need to change the prediction or be wrong.

Agreed. I haven't advanced it to that level yet, but will likely need to and haven't quite parsed out how that would work yet. For the first go, I'm trying to get it up and running at steady state speed and then focus on how to handle speed transitions. If you have any ideas, I'm all ears.

wonder1001:
For the first go, I'm trying to get it up and running at steady state speed and then focus on how to handle speed transitions. If you have any ideas, I'm all ears.

This is commonly done with an incremental encoder but could be as basic as sensing a piece of reflective tape on the end of a shaft with a photoelectric.

The first idea to make it easier and more reliable, is not to use interrupts.

If not interrupts, then what?

Code.

In loop().