Slot Car 4 lane lap Counter - 4 Interrupts

Hello everyone. A few weeks back I requested some help in Project guidance regarding my Arduino Uno project for counting a four lane slot car race. The advice was quite good. The best advice I have seen is trying to describe the problem fully, so here it goes:

PROJECT GOAL:
-Display and accurately count laps for 1/32 slot cars passing over the start finish line in a real time race.
-Check for race end and display final placings for each lane.
-Provide Visual or Audible feedback for race start and end.

PROTOTYPE COMPLETED AND EVOLUTION:
-I completed a working prototype with the Arduino UNO and utilized a traditional "light bar" for counting the passing cars. A light bar in slot car lingo is a series of overhead lights that the car passes under. The car's shadow is sensed underneath the track and is processed from there.

-The prototype worked fine, but at actual race speeds (very fast), the car would not be counted. Via my own research I realized that my photoresistors might be reacting too slow or my code was too un-optimized. Turning to these forums I was told about "interrupts", and it was pointed out that indeed my code was too busy doing other adminstrative duties rather than looking for cars passing. I have since educated myself on the use of interrupts and I am working on a newer second prototype. My research led me to an alternative Arduino, the MEGA 2650 due to the 6 interrupts available to me, which would give one for each of the 4 lanes. I have substituted photoresistors on a benchtop breadboard with momentary switches that trigger one of four ISRs thus simulating a car triggering a sensor. (I am waiting on a slot photointerruptors to arrive any day for sensing).

THE PROBLEM:
-One of the four - - IDENTICAL - - ISRs is working as intended and counting laps as one would expect for a momentary switch. That is, there is some bounce, but I will take care of that later. 3 of the 4 ISRs are causing immediate interrupts and counting down the laps for 3 of the 4 lanes almost immediately after starting the race to zero. I have isolated it to one working pin (D2). Now, I have noted that the LCD takes up 2 of the 6 pins for SDA and SCL (Pins20 and 21), but I don't know if that has anything to do with my problem.

I have attached the code in full, but below is a list of what i feel are germane parts of the code:

// Global Variable Declarations prior to setup

LiquidCrystal_I2C lcd(0x27, 20, 4);     //Sets LCD address plus 20 Char long. 4 Char tall.

volatile int Lane1_Lap = 0;          //Lap Counts Changed in Interrupt Routines
volatile int Lane2_Lap = 0;
volatile int Lane3_Lap = 0;
volatile int Lane4_Lap = 0;

const int Lane1_Sens = 2;              //Interrupt Pins Declaration
const int Lane2_Sens = 3;
const int Lane3_Sens = 18;
const int Lane4_Sens = 19;

//These are the final lines of the void setup

 attachInterrupt(digitalPinToInterrupt(Lane1_Sens), Lane1_Trigger, FALLING);
 attachInterrupt(digitalPinToInterrupt(Lane2_Sens), Lane2_Trigger, FALLING);
 attachInterrupt(digitalPinToInterrupt(Lane3_Sens), Lane3_Trigger, FALLING);
 attachInterrupt(digitalPinToInterrupt(Lane4_Sens), Lane4_Trigger, FALLING);
}

// These are the four ISRs

void Lane1_Trigger(){
 Lane1_Lap = Lane1_Lap -1;
   if (Lane1_Lap <= 0){           //Argument prevents negative laps            
   Lane1_Lap = 0;
   Finisher[0]=0;                     //4 unit Array for placings (bubble sorted in void loop) 
   }
}

void Lane2_Trigger(){
 Lane2_Lap = Lane2_Lap - 1;
   if (Lane2_Lap <= 0){
   Lane2_Lap = 0;
   Finisher[1]=0;
   }
}

void Lane3_Trigger(){
 Lane3_Lap = Lane3_Lap - 1;
   if (Lane3_Lap <= 0){
   Lane3_Lap = 0;
   Finisher[2]=0;
   }
}

void Lane4_Trigger(){
 Lane4_Lap = Lane4_Lap - 1;
   if (Lane4_Lap <= 0){
   Lane4_Lap = 0;
   Finisher[3]=0;
 }
}

4LaneCounter_V006.ino (11.7 KB)

It is unlikely that you need to use interrupts.

What else does the loop() function do that prevents the detectors being polled fast enough to count the cars in loop() ? If you do need to use interrupts then do nothing in the ISR except flag that a car has passed and deal with counting, scoring etc in loop()

Thanks bob. The code has changed from its original form here it is in its entirety.

//================================================================
//         VOID LOOP (RACE)
//================================================================

void loop(){

 //===============================Check for Race End
 if ((Lane1_Lap==0)||(Lane2_Lap==0)||(Lane3_Lap==0)||(Lane4_Lap==0)){
     RaceEnd=1;
     int CurrentPlacing = 0;                      
     for(int f=0;f<4;f++){
       if (Finisher[f] == 0){
         CurrentPlacing=CurrentPlacing+1;
       }
     }
     if ((Lane1_Lap == 0)&&(L1Place==5)){
       L1Place=CurrentPlacing;
     }
     if ((Lane2_Lap == 0)&&(L2Place==5)){
       L2Place=CurrentPlacing;
     }
     if ((Lane3_Lap == 0)&&(L3Place==5)){
       L3Place=CurrentPlacing;
     }
     if ((Lane4_Lap == 0)&&(L4Place==5)){
       L4Place=CurrentPlacing;
     }

 }

 //===============================Displays Remaining Laps to LCD
 
 if (Lane1_Lap ==0){
   lcd.setCursor ( 0, 0 );            
   lcd.print("L1 Place:           ");
   lcd.setCursor ( 10, 0 );            
   lcd.print(L1Place);
   digitalWrite(lane1LED, HIGH);
 }
 if (Lane1_Lap !=0){
   lcd.setCursor ( 0, 0 );            
   lcd.print("L1 Laps Left:        ");
   lcd.setCursor ( 14, 0 );
   lcd.print(Lane1_Lap);
 }            
 
 if (Lane2_Lap ==0){
   lcd.setCursor ( 0, 1 );            
   lcd.print("L2 Place:           ");
   lcd.setCursor ( 10, 1 );            
   lcd.print(L2Place);
   digitalWrite(lane2LED, HIGH);
 }
 if (Lane2_Lap !=0){
   lcd.setCursor ( 0, 1 );            
   lcd.print("L2 Laps Left:       ");
   lcd.setCursor ( 14, 1 );
   lcd.print(Lane2_Lap);
 } 
            
 if (Lane3_Lap ==0){
   lcd.setCursor ( 0, 2 );            
   lcd.print("L3 Place:           ");
   lcd.setCursor ( 10, 2 );            
   lcd.print(L3Place);
   digitalWrite(lane3LED, HIGH);
 }
 if (Lane3_Lap !=0){
   lcd.setCursor ( 0, 2 );            
   lcd.print("L3 Laps Left:       ");
   lcd.setCursor ( 14, 2 );
   lcd.print(Lane3_Lap);
 }  
           
 if (Lane4_Lap ==0){
   lcd.setCursor ( 0, 3 );            
   lcd.print("L4 Place:           ");
   lcd.setCursor ( 10, 3 );            
   lcd.print(L4Place);
   digitalWrite(lane4LED, HIGH);
 }
 if (Lane4_Lap !=0){
   lcd.setCursor ( 0, 3 );            
   lcd.print("L4 Laps Left:       ");
   lcd.setCursor ( 14, 3 );
   lcd.print(Lane4_Lap);
 }            
}

Changed as bob suggested. I made the ISRs only have an event happening that is returned to void loop. I can "see" them counting down now, but they are still doing so automatically.

Please post the whole sketch as it is now

Bob,

I cant post the sketch as it apparently is too long. Regardless, it is currently working. My problem was simple and stupid. The breadboard I was using is larger than my previous one and I didn't realize I needed to jumper the long + and - as I thought it went full length. After sleeping on it I thought it might have been a pullup resistor problem and added those. Didn't work. Then I started going through it with my multi meter. Bone head move. If you have any style tips on my program I would be open to them. I will probably try to add a best lap time to the race loop as well using the mills() function, but that's later. I know it won't give "true times" due to the interrupts but it's not going on the space shuttle so I think it'll be fine.

Thank you for listening last night and helping me talk it thru.

I have attached my code in a file to this post.

NOTE: If anyone is reading this in the future, this code should work, but the switches or photointerrupters or whatever you choose for sensors may need to be debounced.

4LaneCounter_V007.ino (12.5 KB)

I am glad that you got it working

I didn't realize I needed to jumper the long + and - as I thought it went full length.

BTDTGTTS

I dumped my breadboard that has such a "feature"

As an example, here is your code using arrays to simplify things and also just polling the sensors...

//==================================
//       INCLUDED LIBRARIES
//==================================

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//==================================
//        GLOBAL VARIABLES
//==================================

LiquidCrystal_I2C lcd(0x27, 20, 4);     //Sets LCD address plus 20 Char long. 4 Char tall.

const int laneCount = 4;
int Lane_Lap[laneCount];

const int sensorPin[laneCount] = { 2, 3, 18, 19 };

const int enterButtonPin = 4;
const int nextButtonPin = 5;
const int plusButtonPin = 6;

int enterButton;
int nextButton;
int plusButton;

const int laneLEDPin[laneCount] = { 13, 12, 8, 7 };

int Place[laneCount];
int currentFinish = 1;

//======================================================================
//                              SETUP
//======================================================================

void setup() {

  pinMode(enterButtonPin, INPUT);                //ENTER BUTTON
  pinMode(nextButtonPin, INPUT);                //NEXT (+1)BUTTON
  pinMode(plusButtonPin, INPUT);                //PLUS (+10)BUTTON
  for ( int i = 0; i < laneCount; ++i ) {
    pinMode(sensorPin[i], INPUT_PULLUP);
    pinMode(laneLEDPin[i], OUTPUT);
  }

  lcd.init();                            //initialize the lcd
  lcd.backlight();                       //open the backlight
  lcd.setCursor(0, 0);
  lcd.print(" STATE of JEFFERSON ");
  lcd.setCursor(0, 1);
  lcd.print("         XX         ");
  lcd.setCursor(0, 2);
  lcd.print("      SPEEDWAY      ");
  lcd.setCursor(0, 3);
  lcd.print("   Press  <Enter>   ");

  do {
    enterButton = digitalRead(enterButtonPin);
  } while (enterButton == LOW);
  int RequestedLaps = Lap_Menu();
  for (int i = 0; i < laneCount; ++i) Lane_Lap[i] = RequestedLaps;

  //======================================================================
  //                        AWAIT RACE START LOOP
  //======================================================================
  lcd.clear();
  lcd.setCursor ( 0, 0 );
  lcd.print("Race Length: ");
  lcd.print(RequestedLaps);
  lcd.setCursor ( 0, 1 );
  lcd.print("   Place cars on    ");
  lcd.setCursor ( 0, 2);
  lcd.print("   Start / Finish   ");
  lcd.setCursor ( 0, 3 );
  lcd.print("<Enter> begin Race! ");

  do {
    enterButton = digitalRead(enterButtonPin);
  } while (enterButton == LOW );
  for (int i = 0; i < laneCount; ++i ) digitalWrite(laneLEDPin[i], HIGH);

  digitalWrite(laneLEDPin[0], HIGH);
  digitalWrite(laneLEDPin[1], LOW);
  digitalWrite(laneLEDPin[2], LOW);
  digitalWrite(laneLEDPin[3], LOW);
  lcd.clear();
  lcd.setCursor ( 0, 0 );
  lcd.print("START YOUR ENGINES!");
  delay(1000);

  digitalWrite(laneLEDPin[0], LOW);
  digitalWrite(laneLEDPin[1], HIGH);
  digitalWrite(laneLEDPin[2], LOW);
  digitalWrite(laneLEDPin[3], LOW);
  lcd.setCursor ( 0, 1 );
  lcd.print("       READY!");
  delay(1000);

  digitalWrite(laneLEDPin[0], LOW);
  digitalWrite(laneLEDPin[1], LOW);
  digitalWrite(laneLEDPin[2], HIGH);
  digitalWrite(laneLEDPin[3], LOW);
  lcd.setCursor ( 0, 2);
  lcd.print("         SET!");
  delay(1000);

  digitalWrite(laneLEDPin[0], LOW);
  digitalWrite(laneLEDPin[1], LOW);
  digitalWrite(laneLEDPin[2], LOW);
  digitalWrite(laneLEDPin[3], HIGH);
  lcd.setCursor ( 0, 3 );
  lcd.print("          GO!");
  delay(1000);
  digitalWrite(laneLEDPin[3], LOW);
}

//======================================================================
//         VOID LOOP (RACE)
//======================================================================

void loop() {

  for (int i = 0; i < laneCount; ++i) {
    if ( Lane_Lap[i] ) {
      // car not done, check sensors
      if ( digitalRead(sensorPin[i]) == LOW) {
        Lane_Lap[i]--;
        if (Lane_Lap[i] == 0) {
          // this car is finished, record it
          Place[i] = currentFinish;
          currentFinish++;
        }
      }
    }
  }

  //===============================Displays Remaining Laps to LCD
  for (int i = 0; i < laneCount; ++i ) {
    lcd.setCursor ( 0, i );
    lcd.print("L");
    lcd.print(i);
    if (Lane_Lap[i] == 0) {
      lcd.print(" Place:           ");
      lcd.setCursor ( 10, i );
      lcd.print(Place[i]);
      digitalWrite(laneLEDPin[i], HIGH);
    }
    else {
      lcd.print(" Laps Left:        ");
      lcd.setCursor ( 14, i );
      lcd.print(Lane_Lap[i]);
    }
  }
}

//======================================================================
//                             FUNCTIONS
//======================================================================

//======================================================================
//                        Lap_Menu() Function
//======================================================================
int Lap_Menu()
{
  int ChosenLaps = 0;
  lcd.setCursor ( 0, 0 );
  lcd.print("A Race for Laps:    ");
  lcd.setCursor ( 0, 1 );
  lcd.print(" - 25 Laps? <Enter> ");
  lcd.setCursor ( 0, 2 );
  lcd.print(" - Custom? <Next>   ");
  lcd.setCursor ( 0, 3 );
  lcd.print("                    ");
  delay(500);
  while (ChosenLaps == 0) {
    enterButton = digitalRead(enterButtonPin);                 //Pin 4 is <Enter>
    nextButton = digitalRead(nextButtonPin);                  //Pin 5 is <Next>

    if (enterButton == HIGH) {
      ChosenLaps = 25;
    }
    if (nextButton == HIGH) {
      ChosenLaps = Custom_Number();
    }
  }
  delay(500);
  return ChosenLaps;
}

//======================================================================
//                         Custom_Number() Function
//======================================================================
int Custom_Number() {
  int ChosenNumber = 0;
  int firstDigit = 0;
  int secondDigit = 0;
  lcd.setCursor ( 0, 0 );
  lcd.print("Custom Number:    00");
  lcd.setCursor ( 0, 1 );
  lcd.print(" Press <+>    +10   ");
  lcd.setCursor ( 0, 2 );
  lcd.print(" Press <Next> +1    ");
  lcd.setCursor ( 0, 3 );
  lcd.print(" When done: <Enter> ");

  while ((enterButton == LOW)) {
    enterButton = digitalRead(enterButtonPin);                 //Pin 4 is <Enter>
    nextButton = digitalRead(nextButtonPin);                  //Pin 5 is <Next>
    plusButton = digitalRead(plusButtonPin);                  //Pin 6 is <Plus>
    if (plusButton == HIGH) {
      firstDigit = firstDigit + 1;
      lcd.setCursor ( 0, 0 );
      lcd.print("Custom Number:    ");
      lcd.setCursor ( 18, 0 );
      lcd.print(firstDigit);
      lcd.setCursor ( 19, 0 );
      lcd.print(secondDigit);

      delay(500);
      if (firstDigit > 9) {
        firstDigit = 0;
        lcd.setCursor ( 0, 0 );
        lcd.print("Custom Number:    ");
        lcd.setCursor ( 18, 0 );
        lcd.print(firstDigit);
        lcd.setCursor ( 19, 0 );
        lcd.print(secondDigit);
        delay(500);
      }
    }
    else if (nextButton == HIGH) {
      secondDigit = secondDigit + 1;
      lcd.setCursor ( 0, 0 );
      lcd.print("Custom Number:    ");
      lcd.setCursor ( 18, 0 );
      lcd.print(firstDigit);
      lcd.setCursor ( 19, 0 );
      lcd.print(secondDigit);
      lcd.setCursor(0, 2);                // Weird Code Gremlin Correction that...
      lcd.print(" ");                     // ...just keeps a zero from appearing
      delay(500);
      if (secondDigit > 9) {
        secondDigit = 0;
        lcd.setCursor ( 0, 0 );
        lcd.print("Custom Number:    ");
        lcd.setCursor ( 18, 0 );
        lcd.print(firstDigit);
        lcd.setCursor ( 19, 0 );
        lcd.print(secondDigit);
        delay(500);
      }
    }
  }

  delay(500);
  ChosenNumber = 10 * firstDigit + secondDigit;
  return ChosenNumber;
}