Flash LED without disrupting interrupt routines

I have a sketch that uses a laser and detector to count vehicles on the highway. When a vehicle blocks the path between the two, a determination is made, based on the speed limit, what that vehicle type is. Eg, a motorcycle is shorter than a bus so takes less time to transit. I use interrupts to trigger when the beam is broke.
This works, but the LEDs I'm using to tell me what vehicle went by don't stay on long enough, even though at this point I'm still using the delay function of 1/2 second to keep the LED on. What I want to do is:

  1. detect vehicle
  2. light appropriate LED and keep it on for 1/2 second to 1 second.
  3. if another vehicle breaks the beam during that period, keep the previous LED on anyway and light the second LED. Now if both vehicles are the same, a very likely scenario where two are passing simultaneously, I would just store the event and not display anything.

I will be adding RTC and SD card memory soon. I have that worked out already in another project. This car counter will be "unmanned" and I will gather information once a day from the card (or radio), so I don't really NEED the LEDs....this is more for testing, to show people how it works.

At top of my sketch are the transit times. At very bottom is the LED function call that I want to modify. I am familiar (somewhat) with using millis for this part of the code.

    /*  Name: LaserVehicleDetection21FE  This program works 10Dec2019
        By:            Bob Found, Indian Harbour, NS Canada
        Start Date:    30Nov2019
        Last Rev:      19Dec2019
        MCU:           Arduino UNO
        Hardware:      UNO, laser, laser detector relay
        Description: Laser and digital sensor with relay.
        Vers History: V1.0  Simple test version
        Vers 1.1 Change name to LaserVehicleDetection, added notes
        1.2 use speed and vehicle size to determine vehicle type
        1.5 use interrupts for laser detection, polling not fast enough
        2.0 Used code from Nick Gammon for slot car race timer for proper logic.
        2.1 cleanup, used own variables, took out superfluous code
        2.2 Created array for 5 vehicles to minimize code, passed two parameters to
            2 created functions: printResult and lightLED  One LED for each vehicle
    
        Transit times based on average length of vehicle shown in table,
        at 3 speed limits.  Very crude determination because speed is assumed,
        not necessarily followed.  Speed not measured (another project).  Values 
        shown are absolutely correct).
    
                                   11.1M/sec       13.9M/sec     16.7M/sec
                                    40kph          50kph         60kph
      Vehicle      length  length  Transit time  Transit time  Transit time
                    meters  feet       sec            sec          sec
      motorcycle     2.5    8.25      0.23           0.18         0.013
      car            4.5    14.85     0.41           0.32         0.024
      SUV            5.2    17.16     0.47           0.37         0.028
      Pickup         6.6    21.78     0.59           0.47         0.036
      Bus, semi      14     46.2      1.26           1.01         0.076
    */
    const byte RelayPin =  2;  // pin 2 or 3 for laser detector relay interrupt input
    const byte LEDCycle =  8;  //motorcycle LED
    const byte LEDCar =    9;  //car
    const byte LEDSUV =    10; //SUV
    const byte LEDPickup = 11; //pickup
    const byte LEDBus =    12; //bus
    unsigned long lastTriggerTime;
    volatile unsigned long triggerTime;//used in main code and ISR, therefore volatile
    volatile boolean triggered;
    bool ledoff = HIGH; //inverted logic for LED since tied to current-sink
    bool ledon = LOW;
    float seconds;
    String vehicle;
    byte VehicArray [5] = {LEDCycle, LEDCar, LEDSUV, LEDPickup, LEDBus};
    
    //*****************Interrupt Service Routine******************
    void isr ()
    { // wait until we noticed last one
      if (triggered)
        return;
      triggerTime = micros ();
      triggered = true;
    }
    //*******************Setup Function*****************************
    void setup() {
      for (byte x = 0; x < 5; x++)
      { pinMode(VehicArray[x], OUTPUT);//Set all vehicle LED pins(in array) to Output
        digitalWrite(VehicArray[x], ledoff);}//Initialize LEDs (off) for all vehicles
      Serial.begin (9600);  
      pinMode(RelayPin, INPUT_PULLUP);//define laser relay input pin
      digitalWrite (RelayPin, HIGH);
      attachInterrupt(digitalPinToInterrupt (RelayPin), isr, RISING);
      Serial.println ("Waiting for vehicle...");   
    }
    //**********************Loop Function***************************
    void loop()
    {{ if (!triggered)//Wait for interrupt
          return;//loop here until trigger is true (interrupt occurred), then execute:
      }
      noInterrupts();
      unsigned long elapsed = triggerTime - lastTriggerTime;
      lastTriggerTime = triggerTime;
      triggered = false;  // re-arm for next time
      float seconds = elapsed / 1000000.000;
      if (seconds < 0.3)
      { //printResult("Motorcycle ", seconds);//printing is only used for testing
        lightLED(LEDCycle);
      }
      else if
      (seconds > 0.3 && seconds < 0.450)
      { //printResult("Car  ", seconds);
        lightLED(LEDCar);
      }
      else if
      (seconds > 0.450 && seconds < 0.530)
      {// printResult("SUV  ", seconds);
        lightLED(LEDSUV);
      }
      else if
      (seconds > 0.530 && seconds < 0.700)
      { //printResult("Pickup  ", seconds);
        lightLED(LEDPickup);
      }
      else if
      (seconds > 0.700)
      { //printResult("Bus  ", seconds);
        lightLED(LEDBus);
      }
      interrupts();
    }
    //***************printResult Function********************
    //for testing only
    void printResult(String vehicle, float seconds)
    { Serial.print(vehicle);
      Serial.println(seconds);
    }
    //***************lightLED Function***********************
    void lightLED(byte vehicLED)
    { digitalWrite (vehicLED, ledon);
      delay (500);//should run millis here 
      digitalWrite (vehicLED, ledoff);
      delay (500);
    }

Why are you using
a) interrupts
b) delays?

Consider doing this without interrupts and using millis() timing rather than delays.

This compiles but is not tested:

/*  Name: LaserVehicleDetection21FE  This program works 10Dec2019
    By:            Bob Found, Indian Harbour, NS Canada
    Start Date:    30Nov2019
    Last Rev:      19Dec2019
    MCU:           Arduino UNO
    Hardware:      UNO, laser, laser detector relay
    Description: Laser and digital sensor with relay.
    Vers History: V1.0  Simple test version
    Vers 1.1 Change name to LaserVehicleDetection, added notes
    1.2 use speed and vehicle size to determine vehicle type
    1.5 use interrupts for laser detection, polling not fast enough
    2.0 Used code from Nick Gammon for slot car race timer for proper logic.
    2.1 cleanup, used own variables, took out superfluous code
    2.2 Created array for 5 vehicles to minimize code, passed two parameters to
        2 created functions: printResult and lightLED  One LED for each vehicle

    Transit times based on average length of vehicle shown in table,
    at 3 speed limits.  Very crude determination because speed is assumed,
    not necessarily followed.  Speed not measured (another project).  Values
    shown are absolutely correct).

                               11.1M/sec       13.9M/sec     16.7M/sec
                                40kph          50kph         60kph
  Vehicle      length  length  Transit time  Transit time  Transit time
                meters  feet       sec            sec          sec
  motorcycle     2.5    8.25      0.23           0.18         0.013
  car            4.5    14.85     0.41           0.32         0.024
  SUV            5.2    17.16     0.47           0.37         0.028
  Pickup         6.6    21.78     0.59           0.47         0.036
  Bus, semi      14     46.2      1.26           1.01         0.076
*/

#define NUM_VEH_TYPES           5
#define KLED_OFF                HIGH
#define KLED_ON                 LOW
#define START_TRIGGER_LEVEL     HIGH        //or LOW, whatever your logic

const byte RelayPin =  2;  // pin 2 or 3 for laser detector relay interrupt input
const byte LEDCycle =  8;  //motorcycle LED
const byte LEDCar =    9;  //car
const byte LEDSUV =    10; //SUV
const byte LEDPickup = 11; //pickup
const byte LEDBus =    12; //bus

//String vehicle;

unsigned long grTimes[NUM_VEH_TYPES][2] = 
{
    {0, 300},
    {300, 450},
    {450, 530},
    {530, 700},
    {700, 1500}    
        
};

byte lastSw;

typedef struct structVehicleLEDs
{
    byte            pinNo;
    bool            bActive;
    unsigned long   timeLEDStart;
    unsigned long   timeLEDHold;
    
}sVehicleLEDs;

sVehicleLEDs VehicleLEDs[NUM_VEH_TYPES] =
{
    {
        .pinNo = LEDCycle,
        .bActive = false,
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDCar,
        .bActive = false,
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDSUV,
        .bActive = false,
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDPickup,
        .bActive = false,
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDBus,
        .bActive = false,
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    }
    
};

//*******************Setup Function*****************************
void setup() 
{          
    for( byte x=0; x<5; x++ )
    { 
        pinMode( VehicleLEDs[x].pinNo, OUTPUT );        //Set all vehicle LED pins(in array) to Output
        digitalWrite( VehicleLEDs[x].pinNo, KLED_OFF ); //Initialize LEDs (off) for all vehicles        
        
    }//for
    
    Serial.begin (9600); 
    
    pinMode(RelayPin, INPUT_PULLUP);//define laser relay input pin    
    digitalWrite (RelayPin, HIGH);
    lastSw = digitalRead( RelayPin );
        
    Serial.println ("Waiting for vehicle...");   
    
}//setup

//**********************Loop Function***************************
void loop()
{
    VehicleTimer();
    LED_StateMachine();
    
}//loop

void VehicleTimer( void )
{
    byte
        nSw;
    static unsigned long
        timeStart;
    unsigned long
        timeVehicle;

    nSw = digitalRead( RelayPin );
    if( nSw != lastSw )
    {
        lastSw = nSw;
        if( nSw == START_TRIGGER_LEVEL )
        {
            //start timing
            timeStart = millis();            
            
        }//if
        else
        {
            timeVehicle = millis() - timeStart;
            for( int i=0; i<NUM_VEH_TYPES; i++ )
            {
                if( timeVehicle >= grTimes[i][0] && timeVehicle < grTimes[i][1] )
                {
                    //turn on LED and set time
                    digitalWrite( VehicleLEDs[i].pinNo, KLED_ON );
                    VehicleLEDs[i].bActive = true;
                    VehicleLEDs[i].timeLEDStart = millis();
                    
                }//if
                
            }//for            
            
        }//else
        
    }//if
    
}//VehicleTimer

void LED_StateMachine( void )
{
    static byte
        idx = 0;

    if( VehicleLEDs[idx].bActive )
    {
        if( (millis() - VehicleLEDs[idx].timeLEDStart) >= VehicleLEDs[idx].timeLEDHold )
        {        
            digitalWrite( VehicleLEDs[idx].pinNo, KLED_OFF );
            VehicleLEDs[idx].bActive = false;
            
        }//if
        
    }//if

    idx++;
    if( idx == NUM_VEH_TYPES )
        idx = 0;
    
}//LED_StateMachine

//***************printResult Function********************
//for testing only
#if 0
void printResult(String vehicle, float seconds)
{ 
    Serial.print(vehicle);
    Serial.println(seconds);
    
}//printResult
#endif

With a transit time as short as 230 mS (motorcycle at 40 kph...25mph), I thought an interrupt was the only thing that would work.

I can't poll fast enough...can I? If I wanted to catch two motorcycles one behind the other, I would have to keep polling, run the storage routine (SD card), light a LED, in 230 mS so I could catch the next one. This doesn't solve my problem of keeping the LED on for 500 mS to show that there WAS a motorcycle crossing.

Nick Gammon had a big interrupt page on his web-site, where I took some of the code from and believe he was right to use interrupts in his slot car sketch.

I'll check that code. Thanks for the effort

I installed the code, compiled it no errors and the bloody thing is working EXACTLY as I wanted!!

How did you do that? Boy you sure know how to write code!! (I'm new to C++, came from BASIC and Motorola assembler background.)

I'll have to go over that sketch to figure out what you did.

So thank you so much. I'll give you credit (take out Nick's name) for your efforts.

Okay, I'm on a roll, will try and get my SD storage going.

queenidog:
With a transit time as short as 230 mS (motorcycle at 40 kph...25mph), I thought an interrupt was the only thing that would work.

The processor is running at 16MHz (62.5 nanoseconds/clock cycle) so it's plenty fast to handle polling for this sort of task. The key is to not tie up the processor's time with needless cycle wasters like delay().

You certainly can use interrupts for this if you like. But for this task, microsecond-accurate timing is not necessary so it's not imperative to preempt current operations to service a vehicle trigger.

Once again, Blackfin, thanks.
I had to go to Google to look up some things like typedef struct and see that this is something quite useful. I learned a ton of stuff from your program..uh...sketch.

One question about the printResult function that you modified with conditional compiler directives, #if. and #endif.

If I wanted to print any of the values, eg "LEDCycle", how would I use the printResult function?

I'm going through the program character by character, most I can digest...

Here's a slightly modified version to print your debug strings. Note that I increased the baud rate to 115200 to speed up the printing process.

You can stop debug printing by commenting out the line "#define DEBUG_PRINTS..."

/*  Name: LaserVehicleDetection21FE  This program works 10Dec2019
    By:            Bob Found, Indian Harbour, NS Canada
    Start Date:    30Nov2019
    Last Rev:      19Dec2019
    MCU:           Arduino UNO
    Hardware:      UNO, laser, laser detector relay
    Description: Laser and digital sensor with relay.
    Vers History: V1.0  Simple test version
    Vers 1.1 Change name to LaserVehicleDetection, added notes
    1.2 use speed and vehicle size to determine vehicle type
    1.5 use interrupts for laser detection, polling not fast enough
    2.0 Used code from Nick Gammon for slot car race timer for proper logic.
    2.1 cleanup, used own variables, took out superfluous code
    2.2 Created array for 5 vehicles to minimize code, passed two parameters to
        2 created functions: printResult and lightLED  One LED for each vehicle

    Transit times based on average length of vehicle shown in table,
    at 3 speed limits.  Very crude determination because speed is assumed,
    not necessarily followed.  Speed not measured (another project).  Values
    shown are absolutely correct).

                               11.1M/sec       13.9M/sec     16.7M/sec
                                40kph          50kph         60kph
  Vehicle      length  length  Transit time  Transit time  Transit time
                meters  feet       sec            sec          sec
  motorcycle     2.5    8.25      0.23           0.18         0.013
  car            4.5    14.85     0.41           0.32         0.024
  SUV            5.2    17.16     0.47           0.37         0.028
  Pickup         6.6    21.78     0.59           0.47         0.036
  Bus, semi      14     46.2      1.26           1.01         0.076
*/

#define NUM_VEH_TYPES           5
#define KLED_OFF                HIGH        //logic level to turn an LED off
#define KLED_ON                 LOW         //logic level to turn on LED on
#define START_TRIGGER_LEVEL     HIGH        //...or LOW, whatever your logic

#define DEBUG_PRINTS            1           //comment out to stop debug prints to serial monitor

const byte RelayPin =  2;  // pin 2 or 3 for laser detector relay interrupt input
const byte LEDCycle =  8;  //motorcycle LED
const byte LEDCar =    9;  //car
const byte LEDSUV =    10; //SUV
const byte LEDPickup = 11; //pickup
const byte LEDBus =    12; //bus

unsigned long grTimes[NUM_VEH_TYPES][2] =
{
    {0, 300},
    {300, 450},
    {450, 530},
    {530, 700},
    {700, 1500}   
       
};

byte lastSw;

typedef struct structVehicleLEDs
{
    byte            pinNo;
    bool            bActive;
    char            *pszVehicleType;
    unsigned long   timeLEDStart;
    unsigned long   timeLEDHold;    
   
}sVehicleLEDs;

sVehicleLEDs VehicleLEDs[NUM_VEH_TYPES] =
{
    {
        .pinNo = LEDCycle,
        .bActive = false,
        .pszVehicleType = "Motorcycle ",
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDCar,
        .bActive = false,
        .pszVehicleType = "Car ",
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDSUV,
        .bActive = false,
        .pszVehicleType = "SUV ",
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDPickup,
        .bActive = false,
        .pszVehicleType = "Pickup ",
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    },
    {
        .pinNo = LEDBus,
        .bActive = false,
        .pszVehicleType = "Bus ",
        .timeLEDStart = 0,
        .timeLEDHold = 500ul
    }
   
};

//*******************Setup Function*****************************
void setup()
{         
    for( byte x=0; x<5; x++ )
    {
        pinMode( VehicleLEDs[x].pinNo, OUTPUT );        //Set all vehicle LED pins(in array) to Output
        digitalWrite( VehicleLEDs[x].pinNo, KLED_OFF ); //Initialize LEDs (off) for all vehicles       
       
    }//for
   
    Serial.begin (115200);
   
    pinMode( RelayPin, INPUT_PULLUP );  //define laser relay input pin       
    lastSw = digitalRead( RelayPin );
       
    Serial.println ("Waiting for vehicle...");   
   
}//setup

//**********************Loop Function***************************
void loop()
{
    VehicleTimer();
    LED_StateMachine();
   
}//loop

void VehicleTimer( void )
{
    byte
        nSw;
    static unsigned long
        timeStart;
    unsigned long
        timeVehicle;

    nSw = digitalRead( RelayPin );
    if( nSw != lastSw )
    {
        lastSw = nSw;
        if( nSw == START_TRIGGER_LEVEL )
        {
            //start timing
            timeStart = millis();           
           
        }//if
        else
        {
            //stop timing
            timeVehicle = millis() - timeStart;
            for( int i=0; i<NUM_VEH_TYPES; i++ )
            {                
                if( timeVehicle >= grTimes[i][0] && timeVehicle < grTimes[i][1] )
                {
                    //turn on LED and set time
                    digitalWrite( VehicleLEDs[i].pinNo, KLED_ON );
                    VehicleLEDs[i].bActive = true;
                    VehicleLEDs[i].timeLEDStart = millis();
#ifdef DEBUG_PRINTS
                    printResult( VehicleLEDs[i].pszVehicleType, timeVehicle );
#endif
                   
                }//if
               
            }//for           
           
        }//else
       
    }//if
   
}//VehicleTimer

void LED_StateMachine( void )
{
    static byte
        idx = 0;

    if( VehicleLEDs[idx].bActive )
    {
        if( (millis() - VehicleLEDs[idx].timeLEDStart) >= VehicleLEDs[idx].timeLEDHold )
        {       
            digitalWrite( VehicleLEDs[idx].pinNo, KLED_OFF );
            VehicleLEDs[idx].bActive = false;
           
        }//if
       
    }//if

    idx++;
    if( idx == NUM_VEH_TYPES )
        idx = 0;
   
}//LED_StateMachine

//***************printResult Function********************
//for testing only

void printResult( char *szVehicle, unsigned long mS )
{
    Serial.print( szVehicle );
    Serial.print( mS );
    Serial.println( "mS" );
   
}//printResult

Wonderful. Thanks. Learned some more stuff.
Going through the program, making changes to see effect so I can understand some of the code, especially the long, multi-parameter ones that refer to arrays.

Also changed #defines to const byte or equivalent at the top, read on Arduino help that const is preferred to #define (#define was a C device, not necessary for C++). It's a minor thing but I want to stay as current as possible.

Thank you. Gave you credit in my header notes.

Blackfin:
Here's a slightly modified version to print your debug strings. Note that I increased the baud rate to 115200 to speed up the printing process.

You can stop debug printing by commenting out the line "#define DEBUG_PRINTS..."

Why litter the code with all those #IFDEF lines? Just test it once in the printResult() function.

//***************printResult Function********************
//for testing only

void printResult( char *szVehicle, unsigned long mS )
{
#ifdef DEBUG_PRINTS
    Serial.print( szVehicle );
    Serial.print( mS );
    Serial.println( "mS" );
#endif
}//printResult

Even easier, put this at the top of the sketch:

#define DEBUG true  //set to true for debug output, false for no debug output
#define Serial if(DEBUG)Serial

If you define DEBUG as false, then Serial.print statements won't print.