jammcooter
Published © GPL3+

RFID Race timer

A race timing system using RFID to time and identify athletes!

AdvancedShowcase (no instructions)6,829
RFID Race timer

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
Obviously
×1
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
One for the RFID button, one for the Start/Stop button
×2
Toggle Switch, Toggle
Toggle Switch, Toggle
From Sparkfun's website
×1
Resistor 10k ohm
Resistor 10k ohm
For connecting the button output to ground
×2
Jumper wires (generic)
Jumper wires (generic)
Make sure you have assorted, you will need male to male, female to female, and male to female.
×1
SparkFun Simultaneous RFID Reader - M6E Nano
SparkFun Simultaneous RFID Reader - M6E Nano
The thing handling all the RFID!
×1
UHF RFID tags
These are the ones I used, they will fit on the bracket down below
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Chip Holder

Schematics

Circuit Diagram

Code

Timer Code

C/C++
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2, 3);
#include "SparkFun_UHF_RFID_Reader.h"
RFID nano;
#define buzzer 9
#define buzzergnd 10

//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;

//switches and such
const int buttonPin = 6;
int buttonPushCounter = 1;
int buttonState = 0;
int lastButtonState = 0;
int onbtn = 9;
int offbtn = 8;

// 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 15
struct DubFilter dub[MaxEPC];


void setup() {
//RFID 
pinMode(onbtn, INPUT);
pinMode(offbtn, INPUT);
pinMode(12, OUTPUT);
pinMode(7, OUTPUT);
pinMode(buzzer, OUTPUT);
pinMode(buzzergnd, OUTPUT);
digitalWrite(buzzergnd, LOW);
Serial.begin(115200);
while(! Serial);

if(setupNano(38400) == false){
  Serial.println("Module failed to respond");
  while (1);
}
nano.setRegion(REGION_NORTHAMERICA);
nano.setReadPower(1500);

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

Serial.println("Ready");
Serial.println("Turn on switch to begin race");
/*while(!Serial.available());
  Serial.read();
    nano.startReading();
    digitalWrite(12, HIGH);
    Serial.println("RFID Reader active. Ready to begin race");
*/
//timing
pinMode(buttonPin, INPUT);

}

void startRace(){
  Serial.print("Race ");
  Serial.print(heat);
  Serial.println(" started");
  heat++;
  flash();
}

//where the magic happens
void runTimer(){
  
  currentMillis = millis();
  seconds = currentMillis - prevMillis;
  float personTime = seconds/1000;

  if(seconds < 100){
    startRace();
  }

  //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("0");
        
        Serial.print(char(nano.msg[31 + x]));
        Serial.print((""));
      }
      unsigned minutes = (personTime + .0005) /60;
      personTime -= minutes * 60;
      Serial.print(" ");
      Serial.print(minutes);
      Serial.print(':');
      if ((personTime + 0.0005) < 10)
    Serial.print('0');
      Serial.println(personTime, 2);
      place++;
     // flash();
     // delay(10);
    }
  }
}

/**
 * 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(){
  tone(buzzer, 2349);
  delay(150);
  noTone(buzzer);
}

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

void loop() {
  // put your main code here, to run repeatedly:
buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    runTimer();
    digitalWrite(7, HIGH);
  }
  else{
   stopTimer();
   digitalWrite(7, LOW);
  }

int onbtnstate = digitalRead(onbtn);
int offbtnstate = digitalRead(offbtn);
if (onbtnstate == HIGH){
  nano.startReading();
  digitalWrite(12, HIGH);
  Serial.println("RFID Reader Active");
  delay(100);
}
if (offbtnstate == HIGH){
  nano.stopReading();
  digitalWrite(12, LOW);
  Serial.println("RFID Reader deactivated");
  delay(100);
}
  
}

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
}

Timing Software

C/C++
Timing Software
/* 
 Welcome!                                                         
Ensure all cables are properly connected.
First, press on the Right Arrow in the upper left hand corner. When
you hover on it, it will say "Upload". Wait for the message 
"Done Uploading" to appear in the bottom of this screen.                        
Then click on the magnifying glass in the upper right corner.
UNABLE TO UPLOAD ERROR? - Make sure your board is connected 
properly. If you receive thhis error, reference the troubleshooting
section of the manual.

System is ready to begin when "Ready" message appears.

TO START A RACE
1. Activate starter arm switch (red toggle)
2. Starter button is now ready to start, push it to start a race OR 
if starter gun is connected, pull the trigger to start a race
3. Deactivate starter arm switch (red toggle)

MID-RACE OPERATION
If split times are desired, press the RFID activation button (silver
button) and wait for all athletes to cross the finish line. To start
the next lap, press the start button. 

TO END A RACE
When athletes are about to finish the race, activate the RFID reader
with the silver activate button. Once all athetes are across, 
turn the RFID reader off by pressing the button again. To end the
timer, activate the starter arm switch and press the start button. 

Questions? Comments? Concerns? 
Contact John McKellar at (208) 201-6279
 
 
 
 
 
 
 DO NOT TYPE IN THIS PROGRAM, ESPECIALLY THE SENSITIVE CODE BELOW. 
 TYPING INTO IT WILL CAUSE THE SYSTEM TO STOP WORKING.
 */
#include <SoftwareSerial.h>
SoftwareSerial softSerial(2, 3);
#include "SparkFun_UHF_RFID_Reader.h"
RFID nano;


//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;
bool gunReady = false;
bool panelReady = false;
int rfidcounter = 1;
#define piezo 10
bool data = true;
int note = 2000;

#define buzzgnd 9

//switches and such
const int buttonPin = 6;
int buttonPushCounter = 1;
int buttonState = 0;
int lastButtonState = 0;
int rfidbtn = 8;
int gun = 13;
int gunCounter = 1;
int gunState = 0;
int lastGunState = 0;
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 40
struct DubFilter dub[MaxEPC];


void setup() {
  
//RFID 
pinMode(gun, INPUT);
pinMode(rfidbtn, INPUT);
pinMode(12, OUTPUT);


digitalWrite(buzzgnd, LOW);

Serial.begin(115200);
while(! Serial);

if(setupNano(38400) == false){
  Serial.println("Module failed to respond");
  while (1);
}

nano.setRegion(REGION_NORTHAMERICA);
nano.setReadPower(2700);

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

Serial.println("Ready");
pinMode(buttonPin, INPUT);

pinMode(piezo, OUTPUT);
pinMode(buzzgnd, OUTPUT);
}
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("0");
        
        Serial.print(char(nano.msg[31 + x]));
        Serial.print((""));
      }
      
      Serial.print(" ");
      Serial.print(minutes);
      Serial.print(':');
      if ((personTime + 0.0005) < 10)
    Serial.print('0');
      Serial.println(personTime, 2);
      
      place++;
tone(piezo, note, 1000); 
tone(12, note, 1000); 
    }
  }
 
}
void playTone(){
      tone(piezo, note, 1000);
      tone(12, note, 1000);

}

/**
 * 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() {
  // put your main code here, to run repeatedly:
  gunState = digitalRead(gun);
buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    if(gunState == HIGH){
      
      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);
      
      
     }
     
}
}
else if(buttonState == LOW){
  if(gunState == HIGH){
    lap();
    tone(piezo, note, 500);
    delay(175);
  }
}

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

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




int rfidbtnstate = digitalRead(rfidbtn);
if (rfidbtnstate == HIGH){
  rfidcounter++;
    if(rfidcounter %2 == 0){
      nano.startReading();
     digitalWrite(7, HIGH);
     digitalWrite(5, LOW);
      
      //Serial.println("RFID reader activated");
      }
    else if(rfidcounter %2 != 0){
      nano.stopReading();
        digitalWrite(7, LOW);
        digitalWrite(5, HIGH);
        //Serial.println("RFID Reader deactivated");
  } 
  
  delay(200);
}


if(Serial.available() > 0){
  char code = Serial.read();
  if(code == '1'){
   nano.startReading();
    digitalWrite(7, HIGH);
    digitalWrite(5, LOW);
    Serial.println("RFID ON");
    
  }
  if(code == '2'){
    
    nano.stopReading();
    digitalWrite(7, LOW);
    digitalWrite(5, HIGH);
    Serial.println("RFID OFF");
  }
}
//end loop

}
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
}

Credits

jammcooter
0 projects • 9 followers
Contact

Comments

Please log in or sign up to comment.