Building a Race and Lap Counter

Hi, I would like to build a RFID Tracker to track a race and lap of runners. I would like some help in the following code that i had modified to my use case. It could already track the RFID Tag and provide the timing, but I just couldn’t get the lap to increase to track their 2nd, 3rd and on going laps.

I am using the Arduino Uno and the Simultaneous RFID Reader M6E as shield.

/*
  Reading multiple RFID tags, simultaneously!
  By: Nathan Seidle @ SparkFun Electronics
  Date: October 3rd, 2016
  https://github.com/sparkfun/Simultaneous_RFID_Tag_Reader

  Constantly reads and outputs any tags heard

  If using the Simultaneous RFID Tag Reader (SRTR) shield, make sure the serial slide
  switch is in the 'SW-UART' position
*/

//Used for transmitting to the device
//If you run into compilation errors regarding this include, see the README
#include <SoftwareSerial.h>

SoftwareSerial softSerial(2, 3);  //RX, TX

#include "SparkFun_UHF_RFID_Reader.h"  //Library for controlling the M6E Nano module
RFID nano;                             //Create instance

bool scanning = true;

//place variables - data placement
int place = 1;
float seconds;
float personTime;
unsigned long currentMillis = 0;
unsigned long prevMillis = millis;
unsigned long minutes = 0;
int heat = 1;
#define piezo 10
int note = 2000;

#define buzzgnd 9

int gunCounter = 1;
int lapno = 1;

// create structure to filter
struct DubFilter {
  uint8_t EPC[12];
  //unsigned long LastTime;     // optionally you could add data to capture
};

// maximum number of unique EPC
#define MaxEPC 30
struct DubFilter dub[MaxEPC];

void setup() {
  Serial.begin(115200);
  while (!Serial)
    ;  //Wait for the serial port to come online

  if (setupNano(38400) == false)  //Configure nano to run at 38400bps
  {
    Serial.println(F("Module failed to respond. Please check wiring."));
    while (1)
      ;  //Freeze!
  }

  nano.setRegion(REGION_NORTHAMERICA);  //Set to North America

  nano.setReadPower(500);  //5.00 dBm. Higher values may caues USB port to brown out
  //Max Read TX Power is 27.00 dBm and may cause temperature-limit throttling

  // init de-dub
  for (uint8_t i = 0; i < MaxEPC; i++)
    dub[i].EPC[0] = 0;

   Serial.println(F("Press '1' to begin and '2' to Stop."));

}

void startRace() {
  lapno = 1;
  Serial.print("Race ");
  Serial.print(heat);
  Serial.println(" started");

  heat++;
  lap();
}

void lap() {
  Serial.print("Lap ");
  Serial.println(lapno);

  place = 1;
  for (uint8_t i = 0; i < MaxEPC; i++)
    dub[i].EPC[0] = 0;

  lapno++;
}

//where the magic happens
void runTimer() {


  currentMillis = millis();
  seconds = currentMillis - prevMillis;
  float personTime = seconds / 1000;
  unsigned minutes = (personTime + .0005) / 60;
  personTime -= minutes * 60;

  //data - THIS IS WHERE IT ALL GOES DOWN
  if (nano.check() == true) {
    byte responseType = nano.parseResponse();

    if (responseType == RESPONSE_IS_TAGFOUND) {

      // it is already in the list

      if (DeDub()) return;

      Serial.print(place);
      Serial.print(" ");
      byte tagEPCBytes = nano.getTagEPCBytes();

      for (byte x = 0 ; x < tagEPCBytes ; x++)
      {
        if (nano.msg[31 + x] < 0x10) Serial.print(F("0")); //Pretty print
        Serial.print(nano.msg[31 + x], HEX);
        Serial.print(F(" "));
      }

      Serial.print(" ");
      Serial.print(minutes);
      Serial.print(':');
      if ((personTime + 0.0005) < 10)
        Serial.print('0');
      Serial.println(personTime, 2);

      place++;

    }
  }
}

/**
 * This routine will check against the table
 * If EPC is already in the table true will be returned.
 * if NOT EPC will be added to the list and false is turned
 */
bool DeDub() {
  uint8_t i, x;
  bool MissMatch = false;

  // get the num EPC bytes
  uint8_t tagEPCBytes = 12;

  // check each entry in the table
  for (i = 0; i < MaxEPC; i++) {

    // if empty entry
    if (dub[i].EPC[0] == 0) break;

    MissMatch = false;
    for (x = 0; x < tagEPCBytes; x++) {

      // check for miss match
      if (nano.msg[31 + x] != dub[i].EPC[x]) {
        MissMatch = true;
        break;
      }
    }

    // A this point we check for MisMatch (false means we have a Match)
    if (!MissMatch) return true;
  }

  // EPC was not in the list already
  if (i == MaxEPC) {
    Serial.println("Table is full\nCan NOT add more");
    
  } else {
    // add to the list
    for (x = 0; x < tagEPCBytes; x++) {
      // Add to the list
      dub[i].EPC[x] = nano.msg[31 + x];
    }
  }

  return (false);
}

void flash() {

}

void stopTimer() {
  seconds = 0;
  place = 1;
  minutes = 0;
  prevMillis = millis();
}

void loop() {

  if (Serial.available() > 0) {
    char code = Serial.read();
    if (code == 'S' || 's') {
        gunCounter == 2;
    } 

    else if (Serial.available() > 0) {
    char code = Serial.read();
    if (code == 'E' || 'e') {
        gunCounter == 1;
      } 
    }
      if (gunCounter % 2 != 0) {
        startRace();
        gunCounter++;
        tone(piezo, note, 1000);

        delay(150);
      }
      else if (gunCounter % 2 == 0) {
        gunCounter++;
        Serial.print("Race ");
        Serial.print(heat - 1);
        Serial.println(" Ended");
        tone(piezo, note, 1000);

        delay(150);
      }

  } 

  if (gunCounter % 2 == 0) {
    runTimer();
  }

  if (gunCounter % 2 != 0) {
    stopTimer();
    for (uint8_t i = 0; i < MaxEPC; i++)
      dub[i].EPC[0] = 0;
  }

  if (Serial.available() > 0) {
    char code = Serial.read();
    if (code == '1') {
      nano.startReading();
      Serial.println("RFID ON");
      gunCounter == 2;
    }
    if (code == '2') {

      nano.stopReading();
      Serial.println("RFID OFF");
      gunCounter == 3;
    }
  }
  //end loop
}


// ============================================================== Below Code are Previous =======================================




// #define MAX_TAGS 10

// byte myEPC[12];  // Add this line

// // Array to store unique tags
// byte epcArray[MAX_TAGS][12];

// int tagCount = 0;

// void loop() {

//   if (scanning) {

//     bool isDuplicate = false;

//     if (nano.check() == true)  //Check to see if any new data has come in from module
//     {
//       byte responseType = nano.parseResponse();  //Break response into tag ID, RSSI, frequency, and timestamp

//       if (responseType == RESPONSE_IS_KEEPALIVE) {
//         Serial.println(F("Scanning"));
//       } else if (responseType == RESPONSE_IS_TAGFOUND) {
//         //If we have a full record we can pull out the fun bits
//         int rssi = nano.getTagRSSI();  //Get the RSSI for this tag read

//         long freq = nano.getTagFreq();  //Get the frequency this tag was detected at

//         long timeStamp = nano.getTagTimestamp();  //Get the time this was read, (ms) since last keep-alive message

//         byte tagEPCBytes = nano.getTagEPCBytes();  //Get the number of bytes of EPC from response

//         // Read tag into myEPC
//         byte myEPClength = sizeof(tagEPCBytes);
//         nano.readTagEPC(tagEPCBytes, myEPClength);  // Use myEPC here

//         // Check for duplicate
//         for (int i = 0; i < tagCount; i++) {
//           if (memcmp(epcArray[i], tagEPCBytes, 12) == 0) {
//             isDuplicate = true;
//             Serial.print("Dulplicate Found ");
//             //          break;
//           }
//         }

//         // Add unique tag to array
//         if (!isDuplicate) {
//           memcpy(epcArray[tagCount], tagEPCBytes, 12);
//           tagCount++;
//         }

//         /*
//       Serial.print(F(" rssi["));
//       Serial.print(rssi);
//       Serial.print(F("]"));

//       Serial.print(F(" freq["));
//       Serial.print(freq);
//       Serial.print(F("]"));
//       Serial.print(F(" time["));
//       Serial.print(timeStamp);
//       Serial.print(F("]"));
// */


//         // Print tags from array
//         for (int i = 0; i < tagCount; i++) {
//           Serial.print("EPC: ");
//           for (byte x = 0; x < tagEPCBytes; x++) {
//             if (nano.msg[31 + x] < 0x10) Serial.print(F("0"));  //Pretty print
//             Serial.print(nano.msg[31 + x], HEX);
//             Serial.print(F(" "));
//           }
//           Serial.print(F("]"));

//           Serial.println();

//           for (int j = 0; j < 12; j++) {
//             Serial.print(epcArray[i][j]);
//             Serial.print(" ");
//           }
//           Serial.println();
//         }

//         /*
//       //Print EPC bytes, this is a subsection of bytes from the response/msg array
//       Serial.print(F(" epc["));
//       for (byte x = 0 ; x < tagEPCBytes ; x++)
//       {
//         if (nano.msg[31 + x] < 0x10) Serial.print(F("0")); //Pretty print
//         Serial.print(nano.msg[31 + x], HEX);
//         Serial.print(F(" "));
//       }
//       Serial.print(F("]"));

//       Serial.println();

//       */
//       } else if (responseType == ERROR_CORRUPT_RESPONSE) {
//         Serial.println("Bad CRC");
//       } else {
//         //Unknown response
//         Serial.print("Unknown error");
//       }
//     }
//   }
//   if (Serial.available()) {
//     char key = Serial.read();
//     if (key == 'S' || key == 's') {
//       scanning = false;
//       Serial.println("Stopped scanning");
//       nano.stopReading();
//     }
//   }

//   if (Serial.available()) {
//     char key = Serial.read();
//     if (key == 'R' || key == 'r') {
//       scanning = true;
//       Serial.println("Start scanning");
//       nano.startReading();
//     }
//   }
// }

//Gracefully handles a reader that is already configured and already reading continuously
//Because Stream does not have a .begin() we have to do this outside the library
boolean setupNano(long baudRate) {
  nano.begin(softSerial);  //Tell the library to communicate over software serial port

  //Test to see if we are already connected to a module
  //This would be the case if the Arduino has been reprogrammed and the module has stayed powered
  softSerial.begin(baudRate);  //For this test, assume module is already at our desired baud rate
  while (softSerial.isListening() == false)
    ;  //Wait for port to open

  //About 200ms from power on the module will send its firmware version at 115200. We need to ignore this.
  while (softSerial.available()) softSerial.read();

  nano.getVersion();

  if (nano.msg[0] == ERROR_WRONG_OPCODE_RESPONSE) {
    //This happens if the baud rate is correct but the module is doing a ccontinuous read
    nano.stopReading();

    Serial.println(F("Module continuously reading. Asking it to stop..."));

    delay(1500);
  } else {
    //The module did not respond so assume it's just been powered on and communicating at 115200bps
    softSerial.begin(115200);  //Start software serial at 115200

    nano.setBaud(baudRate);  //Tell the module to go to the chosen baud rate. Ignore the response msg

    softSerial.begin(baudRate);  //Start the software serial port, this time at user's chosen baud rate

    delay(250);
  }

  //Test the connection
  nano.getVersion();
  if (nano.msg[0] != ALL_GOOD) return (false);  //Something is not right

  //The M6E has these settings no matter what
  nano.setTagProtocol();  //Set protocol to GEN2

  nano.setAntennaPort();  //Set TX/RX antenna ports to 1

  return (true);  //We are ready to rock
}

Thank you

It should be something like:

  1. Define Lap Count Variable: Add a variable to keep track of the lap count. You can declare it globally with other variables at the beginning of your code:
int lapCount = 0;
  1. Update lap() Function: Modify the lap() function to increment the lap count and reset the place counter and the duplicate filter:
void lap() {
  Serial.print("Lap ");
  Serial.println(lapno);

  lapCount++; // Increment the lap count

  place = 1;
  for (uint8_t i = 0; i < MaxEPC; i++)
    dub[i].EPC[0] = 0;

  lapno++;
}
  1. Display Lap Count: In the runTimer() function, after printing the time, display the lap count as well:
Serial.print(" ");
Serial.print("Lap Count: ");
Serial.println(lapCount);
  1. Reset Lap Count: Modify the stopTimer() function to reset the lap count along with other timers and variables:
void stopTimer() {
  seconds = 0;
  place = 1;
  minutes = 0;
  lapCount = 0; // Reset the lap count
  prevMillis = millis();
}
  1. Handle Lap Count in Main Loop: Modify the loop() function to handle lap counting. You can check for lap count updates based on a condition, such as a specific RFID tag. For example, you could modify the runTimer() function to include the lap counting check:
void runTimer() {
  // ... (existing code)

  if (nano.check() == true) {
    // ... (existing code)

    // Check for a specific condition (e.g., specific RFID tag) to increment lap count
    if (/* condition for detecting a new lap */) {
      lap();
    }
  }
}
  1. Remember to replace the comment /* condition for detecting a new lap */ with the appropriate logic to detect when a new lap has started. This could involve checking for a specific RFID tag or some other trigger.