Make Simultaneous RFID reader read a tag once for a race timing system

Hi! I recently purchased the Simultaneous RFID reader and external antenna, connected to my Arduino Uno. I wrote a program to time a race and use the tag’s EPC as the identifier for the person (before the race, I would write the person’s name to the tag). It all works great, except for one problem: the entire time the tag is in the read range of the antenna, the Serial monitor is printing the data. I only want the tag to be printed once on the serial monitor, and I’m not exactly sure how to do that. Any help is much appreciated!

My code:

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




void setup() {
//RFID 
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(2700);

Serial.println("Ready");
Serial.println("Press a key to start RFID Reader");
while(!Serial.available());
  Serial.read();
    nano.startReading();
    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){
      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);
    }
  }
}




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();
}
else{
  stopTimer();
}
}

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
}

Have not tried that is works, but added a DeDub function

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

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

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


void setup() {
//RFID 
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(2700);

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

Serial.println("Ready");
Serial.println("Press a key to start RFID Reader");
while(!Serial.available());
  Serial.read();
    nano.startReading();
    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;
    
    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
      if(dub[i].EPC[x] = nano.msg[31 + x]) continue;
    }
  }
  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();
}
else{
  stopTimer();
}
}

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
}

Thanks for the help! It almost works. The first tag it only prints once, but the second tag it sees it will print ten times and then say “table is full”.

Some small changes… again not tested fully…

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

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


void setup() {
//RFID 
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(2700);

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

Serial.println("Ready");
Serial.println("Press a key to start RFID Reader");
while(!Serial.available());
  Serial.read();
    nano.startReading();
    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(nano.msg[31 + x], HEX);
        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();
  }
  else{
   stopTimer();
  }
}

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
}

Worked perfectly! Thank you so much for all your help!