queue items to be processed in main loop

hello gentleman,
I am trying to write a sketch where I can queue some objects to be processed in the main loop.
I explain: I have a photo eye detector and an encoder installed.
I would like to queue all the objects when a photo eye is hit.
I made the queue but don't know how to link it in the main loop.
in the main loop, I would like to make sure that all the queued objects are detected and processed until the queue is empty.
example:

photo eye detect
object[0] count[0] added to the queue // count[] is the encoder count when a photo eye been activated
photo eye detect
object[1] count[1] added to the queue
photo eye detect
object[2] count[2] added to the queue

and so on,

in the main loop, I would like to do:
if object[n] is not empty process and remove it from the queue.
there will be maximum of 5 queued items, so after that, the queue will start over.

Thank you

Can you post your current code for reference?

Since this appears to be hardware inputs controlled by your software include a schematic, not a frizzy thing with links to the components you used. Be sure to include all power and ground connections.

We had to use queuing on a Burroughs mainframe computer because it was a single processor, but milti-programming. An Arduino is also a single processor, but only one program can ever be running. What is the purpose of your queuing?
Paul

Paul_KD7HB:
We had to use queuing on a Burroughs mainframe computer because it was a single processor, but milti-programming. An Arduino is also a single processor, but only one program can ever be running. What is the purpose of your queuing?
Paul

The queuing is necessary because the objects hit the photocell at a rate bigger than the process rate.
I.e, one item can hit the photocell before the previous one can hit the processing station.

What is a processing station?
Paul

abdelhmimas:
The queuing is necessary because the objects hit the photocell at a rate bigger than the process rate.
I.e, one item can hit the photocell before the previous one can hit the processing station.

How do you know that five is the limit? As ever, it would help our understanding if we knew more about what you're doing.

Paul_KD7HB:
What is a processing station?
Paul

It is a solenoid valves that spray paint, the paint beam can be adjusted based on encoder pulses (distance).
I have three different paint colour solenoid valves at the same spot.
The project is to spray paint on boxes.
The distance from the photocell to the spraying station is bigger than the gap between two boxes, it is the reason I need the queue.
The boxes have the same width and they can arrive randomly but the distance between two consecutive boxes if any is the same.
Thanks

First instinct would be to make a struct that holds the required information for an object. Create an array of these structs of size, say, 8 or 16 to start.

Utilize the array in a circular buffer: Create two variables; a "head" pointer and a "tail" pointer. Initialize them to the same point in the array (e.g. headPtr = tailPtr = 0;)

Each photo eye detection events store each event and advances the head pointer by one. The mainline code checks to see if the circular buffer head and tail pointers are the same; if not, it takes a snapshot of the current head pointer and begins processing all events in the queue, advancing the tail pointer by one each time. When the head (snapshot) and tail are the same all pending events are handled and the mainline "idles" waiting for the two to differ again.

The required size of the circular array of structures is going to depend on the number of events per unit time, the time required to process each event etc.

@blackfin,
The queue side of the code is working as it should be, every time a new product hits the photocells I (enqueue) it properly.
It is the part in the main loop to deal with it where I have no idea.
Any code example will be welcomed.

@blackfin
As an example:
Distance photoeye to solenoid: 800 pulses
Gap between two consecutive boards: 200 pulses

Let’s say we have photocell events at counts:
200,400,600,1000,1200
We should spray at:
1000, 1200, 1400, 1800, 2000

Thank you

ESP32, freeRTOS has message queues. Especially good for cross core communications and background operations such as SPI background receiving and sending.

abdelhmimas:
@blackfin,
The queue side of the code is working as it should be, every time a new product hits the photocells I (enqueue) it properly.
It is the part in the main loop to deal with it where I have no idea.
Any code example will be welcomed.

If you could likewise post your current code (or a minimally demonstrative example of it) that would be appreciated and would make the task of fully understanding what you're doing easier.

Without knowing how you're doing things now, I took a guess. Compiles, not tested.

/*
 * Sketch: sketch_feb22d.ino
 * Target: TBD
 *
 */

#define QUEUE_SIZE              16          //use a power of 2 for easy head/tail math
#define OFFSET_TICKS            800ul       //# of enc ticks from PD to solenoid on
#define OBJECT_DETECTED         LOW         //change to HIGH if needed; depends on PD wiring
#define TIME_READ_PD            50ul        //read the PD every 50mS
#define SOLENOID_ON             HIGH        //logic level to turn the solenoid on
#define SOLENOID_OFF            LOW         //...and off
#define SOLENOID_ACTIVE_TIME    500ul       //solenoid fires for 500mS

const uint8_t pinSolenoid = 8;
const uint8_t pinPhotoDetector = 2;
const uint8_t pinEncoder = 3;

enum statesQueue
{
    ST_IDLE=0,
    ST_WAIT_ENC_TICKS,
    ST_TIME_SOLENOID
};

volatile uint32_t
    encoderTicks;    
uint32_t
    timeNow,
    timePD;
    
uint8_t
    nowPD,
    lastPD,
    headPtr,
    tailPtr;

typedef struct structSolenoidControl
{
    uint32_t    timeStart;
    uint32_t    encoderStartTick;
    
}sSolenoidControl_t;

sSolenoidControl_t
    SolenoidControl_CB[QUEUE_SIZE];

void setup() 
{
    pinMode( pinSolenoid, OUTPUT );
    pinMode( pinPhotoDetector, INPUT_PULLUP );
    pinMode( pinEncoder, INPUT_PULLUP );
    attachInterrupt( digitalPinToInterrupt( pinEncoder ), isrEncoder, RISING );
    
    lastPD = digitalRead( pinPhotoDetector );
    encoderTicks = 0ul;
    headPtr = 0;
    tailPtr = 0;
    
}//setup

void loop() 
{    
    //check the photodetector
    ProcessPD();
    
    //see if the solenoid needs servicing
    ProcessQueue();
    
}//loop

void ProcessPD( void )
{
    timeNow = millis();
    //time to read the photodetector?
    if( (timeNow - timePD) >= TIME_READ_PD )
    {
        //yes; save the time for the next read
        timePD = timeNow;
        //read the sensor
        nowPD = digitalRead( pinPhotoDetector );
        //if it's not the same as last read...
        if( nowPD != lastPD )
        {
            lastPD = nowPD;
            //does it now indicate a detected object?
            if( nowPD == OBJECT_DETECTED )
            {
                //yes; grab the current encoder count and save it in the queue
                //at the head pointer
                noInterrupts();
                SolenoidControl_CB[headPtr].encoderStartTick = encoderTicks;
                interrupts();
                
                //with a new entry, bump the head pointer to the next cell
                //ensure we circle around at the QUEUE_SIZE
                headPtr = (headPtr + 1) & (QUEUE_SIZE - 1);
                
            }//if
                
        }//if
        
    }//if
    
}//ProcessPD

void ProcessQueue( void )
{
    static uint8_t
        stateQueue = ST_IDLE;
    uint32_t
        enTicks,
        timeNow = millis();
        
    switch( stateQueue )
    {
        case    ST_IDLE:
            //if an event as been logged, the head will not equal the tail
            if( tailPtr != headPtr )
            {
                //if so, move to the wait ticks state            
                stateQueue = ST_WAIT_ENC_TICKS;
                
            }//if
            
        break;

        case    ST_WAIT_ENC_TICKS:
            //here we wait for the difference between the current tick count and
            //the tick count when the object was detected (stored it the struct)
            //to get to the offset count.
            //because the encoder ticks up in an ISR, we need to halt interrupts
            //momentarily and grab a copy for use here
            noInterrupts();
            enTicks = encoderTicks;
            interrupts();
            if( (enTicks - SolenoidControl_CB[tailPtr].encoderStartTick) >= OFFSET_TICKS )
            {
                //if that offset number of ticks has occurred, turn on the solenoid and save the time
                digitalWrite( pinSolenoid, SOLENOID_ON );
                SolenoidControl_CB[tailPtr].timeStart = timeNow;
                //then move to time the solenoid firing time
                stateQueue = ST_TIME_SOLENOID;
                
            }//if
            
        break;

        case    ST_TIME_SOLENOID:
            //when the solenoid has been on for the prescribed time...
            if( (timeNow - SolenoidControl_CB[tailPtr].timeStart) >= SOLENOID_ACTIVE_TIME )
            {
                //turn it off and bump the tail pointer
                digitalWrite( pinSolenoid, SOLENOID_OFF );                
                tailPtr = (tailPtr + 1) & (QUEUE_SIZE - 1);
                stateQueue = ST_IDLE;
                
            }//if
        
        break;
        
    }//switch
    
}//ProcessQueue

void isrEncoder( void )
{
    encoderTicks++;
    
}//isrEncoder

Here I define a Queue and a structure to be used in the queue, under freeRTOS.

QueueHandle_t xQ_SERVO_Message;
struct stuSERVO_Message
{
  int MsgID = 1;
  int DLC = 8;
  char Instruction; //
  char p1 = '0';
  char p2 = '0';
  char p3 = '0';
  char p4 = '0';
  char p5 = '0';
  char p6 = '0';
  char p7 = '0';
} xServo_Message;

In setup the queue is assigned a handle, sized for the structure and the number of desired queue's is set.

void setup()
{
xQ_SERVO_Message = xQueueCreate ( 1, sizeof(stuSERVO_Message) );
}

Here is the Queue is populated and set off to the recipient

void fSTOP_walking ( void *pvParameters )
{
  stuSERVO_Message pxServoMessage;
  for ( ;; )
  {
    xEventGroupWaitBits (eg1, evtSTOP_Walking, pdTRUE, pdTRUE, portMAX_DELAY);
    Serial.println ( "fSTOP_walking " );
    if ( EOT == false )
    {
      pxServoMessage.Instruction = '5' ;
      pxServoMessage.p1 = xServo_ACK.p4;
      pxServoMessage.p2 = xServo_ACK.p4;
      pxServoMessage.p3 = xServo_ACK.p4;
      pxServoMessage.p4 = xServo_ACK.p4;
      xQueueSend( xQ_SERVO_Message, ( void * ) &pxServoMessage, QueueReceiveDelayTime );
      EOT = true; // wait for the servo controller to send a task is complete to clear the EOT
    }
  }
  vTaskDelete( NULL );
}// void fSTOP_walking ( void *pvParameters )

The settings used for the queue sending cause a queue copy to be sent leaving the orginal structure settings intact and unaffected by the manipulations of the receiver.

abdelhmimas:
The queuing is necessary because the objects hit the photocell at a rate bigger than the process rate.
I.e, one item can hit the photocell before the previous one can hit the processing station.

Before trying to figure out any code I reckon you need to have a much clearer description of how things are intended to work. For example write down (one line for each item) the sequence of actions that need to happen. Time spent doing this will greatly reduce the time spent writing code at a later stage.

At this stage I can't envisage what your proposed queue is intended to achieve, or when the items are in the queue, what is supposed to happen to them.

My very wild guess is that your project needs a simple state-machine approach rather than a queue.

...R

I think a queue is required. The photocell detects boxes moving towards the paint station. They arrive there 800 encoder counts later and several can be on the way between cell and paint at a time. The system needs to know, for each box when to activate the sprayer(s).

A fancy queue object is probably overkill. Just an array with the value of counts when the box was seen would do.

Edit: Oops - encoder counts, not mS.

here is the part of the code that deals with enqueuing (more complete than above, because it deals with other outputs as well).
the queue is standard one including head and tail pointers and has all the necessary methods as enqueue, dequeue, isEmpty, isFull, already implemented.

the eyetriggered() is the ISR attached to the photocell input FALLING,
currentCount is the encoder count taken from the encoder input ISR.

void eyeTriggered()
{ // photo eye trig event
eyeTriggeredAt = currentCount;
ptr_product[eyeTriggerCount].m1Count = eyeTriggeredAt + distanceEyeM1;
// ptr_product[eyeTriggerCount].m2Count = eyeTriggeredAt + distanceEyeM2;
//ptr_product[eyeTriggerCount].m3Count = eyeTriggeredAt + distanceEyeM3;
eyeTriggerCount++;

if (!qIsFull(last, MAXBRDS))
{
enQueue(queue, &last, eyeTriggeredAt);
}

after this, I dont know how to deal with this in the main loop.

Thanks

}

wildbill:
I think a queue is required. The photocell detects boxes moving towards to the paint station. They arrive there 800mS later and several can be on the way between cell and paint at a time. The system needs to know, for each box when to activate the sprayer(s).

A fancy queue object is probably overkill. Just an array with the value of millis when the box was seen would do.

thanks, I am able to build this array with the encoder count when the box hit the photocell, but I have no clue about how to process this array in the main loop, any suggestions?
Thank you

Just loop through the array looking for a count that's about 800 less than the current value. When you find one, set it zero and fire up the sprayer. Or at least set a flag that says TimeToSpray.

If an array value is zero (or some other special value), ignore it.

You may have to do a bit of extra work when the encoder count rolls over. Alternatively, if you use it in the same pattern as for millis in blink without delay, it may take care of itself.