Timing an RFID tag between two points

Hi All,
I am using the following hardware,

Arduino Mega 2560
ITBD02 moudule
3.2" TFT touchscreen module
ID-12 RFID reader x 2 (The ID-12's are connected to serial 2 & 3)
Momentary switch x 2 (The switches are connected to pins 8 & 9)

Lets say I have an RFID reader and switch at the 'start' and the the same at the 'end'.

What I would like to be able to do is scan an RFID tag at the start and then activate a switch which logs the time interval the switch was activated against that specific tag.
At the end the reverse would happen, a switch would be activated and time logged then the system would wait for an RFID tag to be scanned which will then be associated with the time just logged.
Once an RFID tag has gone through both start and end procedure I would like the time taken to be displayed on the TFT screen in minute/second/milisecond format.

This is the code I have so far, all it does is prove my hardware works, I get time intervals displayed on the screen and RFIDs spat out over serial but im not sure what I need to do next to accomplish my objective.

Any and all feedback and advice much appreciated! Regards Jason.

const int startPin = 8; 
const int finishPin = 9; 

// variables will change:
int buttonState;             // variable for reading the pushbutton status
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

#include <UTFT.h>

// Declare which fonts we will be using
extern uint8_t SmallFont[];

UTFT myGLCD(ITDB32S,38,39,40,41);   // Remember to change the model parameter to suit your display module!

unsigned long time;
unsigned short minutes;
unsigned short seconds;

unsigned long startTime;
unsigned long stopTime;
unsigned long result;

boolean newresult = false;

void setup() {
  
  // initialize the pushbutton pin as an input:
  pinMode(startPin, INPUT);  
  pinMode(finishPin, INPUT); 
  
// Setup the LCD
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);

  
  Serial.begin(9600);    // connect to the serial port for output to serial monitor
  Serial2.begin(9600);   // serial port of RFID reader at start
  Serial3.begin(9600);   // serial port of RFID reader at finish
}

void loop () {
  
  // Clear the screen and draw the frame
  
  time = millis();
  seconds = time / 1000;
  minutes = seconds / 60;
  //result = start - millis();
  
 /* myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(0, 0, 319, 13);
  myGLCD.setColor(64, 64, 64);
  myGLCD.fillRect(0, 226, 319, 239);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(255, 0, 0);
  */
  
 
   // read the state of the switch into a local variable:
  int startState = digitalRead(startPin);
  int finishState = digitalRead(finishPin);
  int reading = 0;

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
  lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:
    buttonState = reading;
  }
  if (startState == HIGH){
    startTime = time;
  }
  
  if (finishState == HIGH){
    result = time - startTime;
    newresult=true;
  }
 
 if (newresult==true){
  myGLCD.setColor(255, 0, 0);
  myGLCD.fillRect(0, 0, 319, 13);
  myGLCD.setColor(64, 64, 64);
  myGLCD.fillRect(0, 226, 319, 239);
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(255, 0, 0);
 
   // set the LED using the state of the button:
   myGLCD.printNumI(result, CENTER, 1);  
 }
  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;
  
  byte i = 0;
  byte val = 0;
  byte code[6];
  byte checksum = 0;
  byte bytesread = 0;
  byte tempbyte = 0;

  if(Serial2.available() > 0) {
    if((val = Serial2.read()) == 2) {                  // check for header 
      bytesread = 0; 
      while (bytesread < 12) {                        // read 10 digit code + 2 digit checksum
        if( Serial2.available() > 0) { 
          val = Serial2.read();
          if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) { // if header or stop bytes before the 10 digit reading 
            break;                                    // stop reading
          }

          // Do Ascii/Hex conversion:
          if ((val >= '0') && (val <= '9')) {
            val = val - '0';
          } else if ((val >= 'A') && (val <= 'F')) {
            val = 10 + val - 'A';
          }

          // Every two hex-digits, add byte to code:
          if (bytesread & 1 == 1) {
            // make some space for this hex-digit by
            // shifting the previous hex-digit with 4 bits to the left:
            code[bytesread >> 1] = (val | (tempbyte << 4));

            if (bytesread >> 1 != 5) {                // If we're at the checksum byte,
              checksum ^= code[bytesread >> 1];       // Calculate the checksum... (XOR)
            };
          } else {
            tempbyte = val;                           // Store the first hex digit first...
          };

          bytesread++;                                // ready to read next digit
        } 
      } 

      // Output to Serial:

      if (bytesread == 12) {                          // if 12 digit read is complete
       // Serial.print("5-byte code: ");
        String thisString;
        if (code[0] < 16){
          thisString = String(0);
          thisString += String(code[0], HEX);
        }
        else {
        thisString = String(code[0], HEX);
        }
        for (i=1; i<5; i++) {
        if (code[i] < 16) thisString += 0;
       thisString += String(code[i], HEX);
       //   Serial.print(code[i]);
         // Serial.print(" ");
        }
        Serial.print(thisString);
        Serial.println();

      //  Serial.print("Checksum: ");
       // Serial.print(code[5], HEX);
       // Serial.println(code[5] == checksum ? " -- passed." : " -- error.");
       // Serial.println();
      }

      bytesread = 0;
    }
  }

if(Serial3.available() > 0) {
    if((val = Serial3.read()) == 2) {                  // check for header 
      bytesread = 0; 
      while (bytesread < 12) {                        // read 10 digit code + 2 digit checksum
        if( Serial3.available() > 0) { 
          val = Serial3.read();
          if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) { // if header or stop bytes before the 10 digit reading 
            break;                                    // stop reading
          }

          // Do Ascii/Hex conversion:
          if ((val >= '0') && (val <= '9')) {
            val = val - '0';
          } else if ((val >= 'A') && (val <= 'F')) {
            val = 10 + val - 'A';
          }

          // Every two hex-digits, add byte to code:
          if (bytesread & 1 == 1) {
            // make some space for this hex-digit by
            // shifting the previous hex-digit with 4 bits to the left:
            code[bytesread >> 1] = (val | (tempbyte << 4));

            if (bytesread >> 1 != 5) {                // If we're at the checksum byte,
              checksum ^= code[bytesread >> 1];       // Calculate the checksum... (XOR)
            };
          } else {
            tempbyte = val;                           // Store the first hex digit first...
          };

          bytesread++;                                // ready to read next digit
        } 
      } 

      // Output to Serial:

      if (bytesread == 12) {                          // if 12 digit read is complete
       
        String thisString;
        if (code[0] < 16){
          thisString = String(0);
          thisString += String(code[0], HEX);
        }
        else {
        thisString = String(code[0], HEX);
        }
        for (i=1; i<5; i++) {
        if (code[i] < 16) thisString += 0;
       thisString += String(code[i], HEX);
     
        }
        Serial.print(thisString);
        Serial.println();

      
      }

      bytesread = 0;
    }
  }
newresult=false;
}

First thing to do is to make two functions

void readRFID(pin) ;
bool RFIDavailable(pin) ;

that takes care of reading the RFID. This code is now twice in your program and that can be simpler.

Q: How many RFID's can be in progress simultaneously?

Q: do they come in the same order? always? sure? :wink:

I would define an array of structs,
and fill the array when it passes the start RFID reader
and at the end search the rfid in the array, display it, and remove from array.

Thank you for your help :slight_smile:

Q: How many RFID's can be in progress simultaneously?

Several, a maximum of 10 in progress at any one time.

Q: do they come in the same order? always? sure?

No, they can end in a different order that they start, some may not ever reach the end.

I realised that I would need to use an array (or several, indexed?) but unsure about how to go about that as I am completely new to this.
What kind of array would I need to be able to cope with the unique HEX code of each tag?

Thanks again!

No, they can end in a different order that they start, some may not ever reach the end.

That means that your array will need to be infinite or you should check that a specific is overdue and remove it from the array.

This is the code outline how I would start to tackle this one: it compiles but it is just the overall structure with several functions to be filled in and others to be written

#define STARTPIN 3
#define ENDPIN 5

struct
{
  char rfid[16];
  uint32_t start;  // start time for determining duration.
} 
progress[25];

int idx = 0;

void setup()
{
  // setup thingies here
}

void loop()
{
  if (RFIDavailable(STARTPIN))
  {
    doStart();
  }

  if (RFIDavailable(ENDPIN))
  {
    doEnd();
  }

  if (RFIDoverdue())
  {
    doOverdue();
  }   
}

bool RFIDavailable(int pin)
{
  if (pin == STARTPIN) return Serial2.available() >=12; // assuming 12 chars in RFID
  if (pin == ENDPIN) return Serial3.available() >=12;
  return false;
}

bool RFIDoverdue()
{
  return false; // dummy
}

void doStart()
{
  // read RFID and add to array
  for (int i=0; i< 12; i++) progress[idx].rfid[i] = Serial2.read();
  progress[idx].rfid[13] = 0; // null terminated string
  progress[idx].start = millis(); 
  idx++;  
}

void doEnd()
{
  // read RFID, 
  // they might be removed due to overdue
  char rfid[16];
  for (int i=0; i< 12; i++) rfid[i] = Serial3.read();
  rfid[13] = 0;
  // search in array
  // if not found ==> error
  // calculate duration   
  // display rfid tag + duration
  // remove rfid from array
  // idx--;
}

void doOverdue()
{
  // handle overdue items in array + remove them
}

Thanks again for your help and advice. I will have a play and post back my results or more likely when im stuck again!

No, they can end in a different order that they start, some may not ever reach the end.

That means that your array will need to be infinite or you should check that a specific is overdue and remove it from the array.

Do you mean like a timeout if the tag has been in progress too long? 10 minutes would be long enough for my application.

yep that's what I meant.
after 10 minutes you must remove the item from the the array, but there is a chance that the item will pop up suddenly :slight_smile:

This is where I am up to so far.

I have copy and pasted bits of what I already had into the template you provided me.

Now i can scan a tag on the start RFID and then push the start button to get the tag ID shown on the serial monitor. There is some behaviour I need to change, it will queue tag reads and then display them all when the start button is pushed, it needs to work with only one tag at a time so it disregards every other read after the first one until the start button has been activated.

With regards to the overdue tags, would I need to specify a time period in milis of more than 600,000 to have them removed from the array after 10 minutes?

The current code is below, I hope I am on the right track!

#define STARTPIN 8
#define ENDPIN 9
#include <UTFT.h>


extern uint8_t SmallFont[];   // Declare which fonts we will be using

UTFT myGLCD(ITDB32S,38,39,40,41);   // Remember to change the model parameter to suit your display module!

struct
{
  char rfid[16];
  uint32_t start;  // start time for determining duration.
} 
progress[25];

int idx = 0;

void setup()
{
 // initialize the pushbutton pin as an input:
  pinMode(STARTPIN, INPUT);  
  pinMode(ENDPIN, INPUT); 
  
// Setup the LCD
  myGLCD.InitLCD();
  myGLCD.setFont(SmallFont);

  
  Serial.begin(9600);    // connect to the serial port for output to serial monitor
  Serial2.begin(9600);   // serial port of RFID reader at start
  Serial3.begin(9600);   // serial port of RFID reader at finish
}

void loop()
{
  if (RFIDavailable(STARTPIN))
  {
    doStart();
  }

  if (RFIDavailable(ENDPIN))
  {
    doEnd();
  }

  if (RFIDoverdue())
  {
    doOverdue();
  }   
}

bool RFIDavailable(int pin)
{  

  int finishState = digitalRead(ENDPIN);
  if (pin == STARTPIN) {
     int startState = digitalRead(STARTPIN);
     if (startState == HIGH) return Serial2.available() > 0; // assuming 12 chars in RFID
  }
  
  if (pin == ENDPIN) return Serial3.available() > 0;
  return false;
}

bool RFIDoverdue()
{
  return false; // dummy
}

void doStart()
{
  // read RFID and add to array
  //for (int i=0; i< 12; i++) progress[idx].rfid[i] = Serial2.read();
  //progress[idx].rfid[13] = 0; // null terminated string
  //progress[idx].start = millis(); 
  //idx++;
  byte i = 0;
  byte val = 0;
  byte code[6];
  byte checksum = 0;
  byte bytesread = 0;
  byte tempbyte = 0;
 
    if((val = Serial2.read()) == 2) {                  // check for header 
      bytesread = 0; 
      while (bytesread < 12) {                        // read 10 digit code + 2 digit checksum
        if( Serial2.available() > 0) { 
          val = Serial2.read();
          if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) { // if header or stop bytes before the 10 digit reading 
            break;                                    // stop reading
          }

          // Do Ascii/Hex conversion:
          if ((val >= '0') && (val <= '9')) {
            val = val - '0';
          } else if ((val >= 'A') && (val <= 'F')) {
            val = 10 + val - 'A';
          }

          // Every two hex-digits, add byte to code:
          if (bytesread & 1 == 1) {
            // make some space for this hex-digit by
            // shifting the previous hex-digit with 4 bits to the left:
            code[bytesread >> 1] = (val | (tempbyte << 4));

            if (bytesread >> 1 != 5) {                // If we're at the checksum byte,
              checksum ^= code[bytesread >> 1];       // Calculate the checksum... (XOR)
            };
          } else {
            tempbyte = val;                           // Store the first hex digit first...
          };

          bytesread++;                                // ready to read next digit
        } 
      } 

      // Output to Serial:
      if (bytesread == 12) {                          // if 12 digit read is complete
       // Serial.print("5-byte code: ");
        String thisString;
        if (code[0] < 16){
          thisString = String(0);
          thisString += String(code[0], HEX);
        }
        else {
        thisString = String(code[0], HEX);
        }
        for (i=1; i<5; i++) {
        if (code[i] < 16) thisString += 0;
       thisString += String(code[i], HEX);

        }
        Serial.print(thisString);
        Serial.println();
      }

      bytesread = 0;
    }
  
}

void doEnd()
{
  // read RFID, 
  // they might be removed due to overdue
  char rfid[16];
  for (int i=0; i< 12; i++) rfid[i] = Serial3.read();
  rfid[13] = 0;
  // search in array
  // if not found ==> error
  // calculate duration   
  // display rfid tag + duration
  // remove rfid from array
  // idx--;
}

void doOverdue()
{
  // handle overdue items in array + remove them
}

I don't know how long an RFID string is but you must wait that they are all available!

Serial2.available() > 0; // assuming 12 chars in RFID

should be

Serial2.available() >= 12; // assuming 12 chars in RFID

otherwise you might do a Serial.Read() from an empty queue resulting in erroronous values.

I have had a go at putting my program logic into flow charts, one for the start and on for the finish. Feedback and criticism appreciated!