Reading cattle EID tags

Hi,

I'm trying to make an EID reader for reading cattle tags. I purchased one of these readers from Priority 1: http://www.priority1design.com.au/shopfront/index.php?main_page=product_info&cPath=1&products_id=37&zenid=hsjm69cqi9cl4v63t2u83tbg41

I have managed to get it hooked up to the Arduino uno and displaying the EID number on an LCD.

My problem is the EID number is not the same as the visual number on the tag.
The EID number looks like this: 982_000175197588, and the visual number on the tag is like this:
UK9 371177 0800 5.

How do I make it display the visual number on the lcd if the tag number is read?

I only have around 70 animals. I'm thinking some sort of table with the tag numbers along with matching visual numbers, but I have no idea how to go about this.

Has anyone any experience with this?

Cheers.

Damien.

1 Like

The EID number looks like this: 982_000175197588

It's hard to believe that a number looks like that.

Your code seems to have vanished in the night.

I used this code with a few changes from user crumpy10

// include the library code:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();
#define WHITE 0x7 //set the backlight colour
SoftwareSerial RFID(2, 3); //pin2 Rx, pin3 Tx

int CMD[64];
int comlen = 0;
int out_flag = 0;

void setup()
{
  Serial.begin(9600);
  lcd.begin(16, 2); // set up the LCD's number of columns and rows:
  RFID.listen();
  Serial.println("Tag number will be displayed here if detected by the module:\n");
  lcd.print("Your tag No is:");
  lcd.setCursor(0, 1); //place cursor on 2nd row for tag number
  
  RFID.begin(9600); // set the data rate for the RFID reader
  delay(10);

}
void loop()

{

  while (Serial.available())
  {
    int a = SerialReadHexDigit();
    if (a >= 0) {
      CMD[comlen] = a;
      comlen++;
    }
    delay(10);
  }

  for (int i = 0; i < comlen; i += 2) {
    int c = RFID.write( CMD[i] * 16 + CMD[i + 1]);
  }
  comlen = 0;

  while (RFID.available()) {
    byte C = RFID.read();
    //if (C<16) Serial.print("0"); //Future use for tags with less numbers
    Serial.write(C);  //Display tag number on serial bus in human form
       
    lcd.write(C);     //Display tag number on LCD
    
    //Serial.print(" "); //Puts space between each digit on Serial Monitor
    out_flag = 1;
  }
  if (out_flag > 0) {
    Serial.println();
    out_flag = 0;
    lcd.setCursor(0, 1);
  }
}
int SerialReadHexDigit()
{
  byte c = (byte) Serial.read();
  if (c >= '0' && c <= '9') {
    return c - '0';
  } else if (c >= 'a' && c <= 'f') {
    return c - 'a' + 10;
  } else if (c >= 'A' && c <= 'F') {
    return c - 'A' + 10;
  } else {
    return -1;   // getting here is bad: it means the character was invalid
  }
}

982_000175197588 is the number format that I get.

Even if I use this simple code:

char val = 0; // variable to store the data from the serial port

 void setup() {
 Serial.begin(9600); // connect to the serial port
 }

 void loop () {
 // read the serial port
 if(Serial.available() > 0) {
 val = Serial.read();
 Serial.write(val);
 }
 }

I get the exact same number and format on the serial monitor.
Is this not correct, what should I be getting?

Damien.

I get the exact same number and format on the serial monitor.

The exact same OUTPUT and the exact same NUMBER are not the same thing. One makes sense. The other does not.

I can't get the pdf file to open, so I can't help with converting the actual output to the expected (what's shown on the tag) output.

You may also be running into some sort of encryption. Some time ago when researching microchips for pets, I discovered that one of the chip manufacturers does some sort of encryption to the tag in their reader so it does not match what it says on the registration information. I don't remember exactly what it was, and if you read their microchip, you would get the same number each time - it just didn't match what your paperwork said. I don't remember right off hand which one it was, but it was one of the common ones out there.

I don't think I explained myself properly. When I say exact same format and number, I mean the 3 digits followed by an underscore, followed by another 12 digits. If I use another tag its in the same format but with a different number.

The first three digits are always the same, which is apparently the country code. Me being the UK.

This is from the pdf of the reader, found on this post Priority 1 Design Rfid - Sensors - Arduino Forum

The output format for a read of an FDX-B protocol transponder is a simple string of decimal characters
indicating the 3 digit country code, and 12 digit National ID followed by the ASCII code $0D (carriage
return) as a string end marker.

When a FDX-B programmed transponder enters the field of the reader it will be scanned and a string as
shown in the example below is transmitted:

999_000000001008, where is serial ASCII code $0D, and "_" is a data separator

In this example 999 is the country code defined inside the transponder, while 000000001008 is the unique
12 digit decimal code used to identify an animal.

FDX-B protocol transponders may contain additional data which can be accessed using specific commands.
See RAT and WAT commands as shown in Table 3. Active Tag Commands Summary further on.

If I use crumpy10's original code I get the tag number including underscore in hex format.
39 38 32 5F 30 30 30 31 37 35 31 39 37 35 38 38 D

These numbers translate into the tag number I originally posted.

My understanding is that the rfid tag number and the visual number printed on the front of the tag do not match, and I somehow have to make them match. Meaning if an rfid tag number is scanned, that it prints the visual number it has been paired with onto the lcd, rather than its own internal number. I have no idea how to do this though.

The visual tag number printed on the tag is a mixture of my heard number and animal number, which is why the internal rfid number does not match, but our business works on the visual numbers.

I hope I've explained things better.

Hope you can help.

Cheers.

My understanding is that the rfid tag number and the visual number printed on the front of the tag do not match, and I somehow have to make them match.

Why? If you scan a tag that says "Purple with pink polka dots" on it, and get the string "654_4412", why is it necessary to construct "Purple with pink polka dots" from "654_4412"?

If, for some reason, it IS necessary, keep two lists in PROGMEM. One would contain "Purple with pink polka dots". The other would contain "654_4412". When you scan a tag, and get "654_4412", look for that in the second list. Use the index to get the corresponding data ("Purple with pink polka dots") from the other list.

Or use an SD card with a file containing entries like "654_4412 = Purple with pink polka dots". Find the record with the right name and use the corresponding value.

Well, then you have to find the magic decoder ring that translates the RFID tag number to the printed one, we cannot help you with that. It is possible there is no connection.

Seedler:
The visual tag number printed on the tag is a mixture of my heard number and animal number, which is why the internal rfid number does not match

From where do those two numbers originate?

You will probably need to create a table, possibly in EEPROM, that relates the RFID number to the number you want. You probably need just the last few digits of the RFID number - just enough to get a unique number for each cow.

PaulS:
Why? If you scan a tag that says "Purple with pink polka dots" on it, and get the string "654_4412", why is it necessary to construct "Purple with pink polka dots" from "654_4412"?

If, for some reason, it IS necessary, keep two lists in PROGMEM. One would contain "Purple with pink polka dots". The other would contain "654_4412". When you scan a tag, and get "654_4412", look for that in the second list. Use the index to get the corresponding data ("Purple with pink polka dots") from the other list.

Or use an SD card with a file containing entries like "654_4412 = Purple with pink polka dots". Find the record with the right name and use the corresponding value.

KeithRB:
You will probably need to create a table, possibly in EEPROM, that relates the RFID number to the number you want. You probably need just the last few digits of the RFID number - just enough to get a unique number for each cow.

Yes this is what I am after. A table that will match the internal tag number to the number I want it to display. How do I go about doing this. Like sd idea because it would be easily updated.

The heard number is my farm business number. The animal number is the unique identifier number within the heard.

I downloaded Priority 1's Reader/Writer application program to see if I could write the number I wanted to the tag, but it doesn't seem to be re-writable.

Damien.

How do I go about doing this.

const int TagCount = N;
char *tagValues[] = { "tagOneValue", "tagTwoValue", /* ..., */ "tagNValue" };
char *tagNames[] = { "tagOneName", "tagTwoName", /* ..., */ "tagNName" };

Then, use a for loop to iterate over the values, comparing the value just read to each of the N tagValues. When you find a match, the tagNames[thatIndex] value will be the name printed on the tag. Break out of the for loop at that point.

PaulS:

const int TagCount = N;

char tagValues[] = { "tagOneValue", "tagTwoValue", / ..., */ "tagNValue" };
char tagNames[] = { "tagOneName", "tagTwoName", / ..., */ "tagNName" };




Then, use a for loop to iterate over the values, comparing the value just read to each of the N tagValues. When you find a match, the tagNames[thatIndex] value will be the name printed on the tag. Break out of the for loop at that point.

PaulS I think this is what I need. I'm still very green a programming. This is what I have put together at the minute, with your suggested code.

#include <Wire.h> 
#include <LCD.h>                     // Standard lcd library
#include <LiquidCrystal_I2C.h> 
#include <SoftwareSerial.h>

SoftwareSerial RFID(2, 3); //pin2 Rx, pin3 Tx

#define I2C_ADDR  0x27               // address found from I2C scanner
#define RS_pin    0                  // pin configuration for LCM1602 interface module
#define RW_pin    1
#define EN_pin    2
#define BACK_pin  3
#define D4_pin    4
#define D5_pin    5
#define D6_pin    6
#define D7_pin    7

LiquidCrystal_I2C lcd(I2C_ADDR, EN_pin, RW_pin, RS_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACK_pin, POSITIVE);
//Pins for the LCD are SCL A5, SDA A4

int out_flag = 0;

const int TagCount = 0;
char *tagValues[] = { "982_000175197588" };
char *tagNames[] = { "0800 5" };

void setup()
{
  Serial.begin(9600);
  lcd.begin(20, 4); // set up the LCD's number of columns and rows:
  RFID.listen();
  Serial.println("Tag number will be displayed here if detected by the module:\n");
  lcd.print("Your tag No is:");
  lcd.setCursor(0, 1); //place cursor on 2nd row for tag number
  
  RFID.begin(9600); // set the data rate for the RFID reader
  delay(10);

}

void loop()
{
  while (RFID.available()) {
    byte C = RFID.read();
    Serial.write(C);  //Display tag number on serial bus in human form
    lcd.write(C);     //Display tag number on LCD
    out_flag = 1;
  }
  if (out_flag > 0) {
    Serial.println();
    out_flag = 0;
    lcd.setCursor(0, 1);
  }
}

Do I need a veriable that will store the whole tag number? I'm guessing the byte C in my code is just reading and printing one digit at a time through the while loop.

How do I write the for loop?

Thanks,

Damien.

const int TagCount = 0;

Then you have data for one tag...

Do I need a veriable that will store the whole tag number?

I thought you already had that. I see that you don't. Yes, you need one (an array of chars, NULL terminated each time you add a character to it). You need an index variable to, to keep track of where to add data to the array.

Your while statement seems to operate on the principal that all the RFID data will arrive at once. That is not the case.

I'm guessing the byte C in my code is just reading and printing one digit at a time through the while loop.

Yes.

How do I write the for loop?

Assuming that you have rfidData as a char array,

   for(byte t=0; t<TagCount; t++)
   {
      if(strcmp(rfidData, tagValues[t]) == 0) // If there is a match
      {
         // Do something with tagNames[t]
         break; // No need to test the other values after finding a match
      }
   }

const int TagCount = 0;

So is this the number of tags I have stored? 0 being one tag, 1 being two?

Will this store the tag number. 16 characters with 16 NULL terminators?

char rfidData[32];

Is this how to add to that variable? With NULL terminator.

rfidData += (C, '\0');

I know that code is wrong because it doesn't work, but am I close?

Cheers for all your help

Damien.

I tried this, but it still doesn't work

#include <Wire.h> 
#include <LCD.h>                     // Standard lcd library
#include <LiquidCrystal_I2C.h> 
#include <SoftwareSerial.h>

SoftwareSerial RFID(2, 3); //pin2 Rx, pin3 Tx

#define I2C_ADDR  0x27               // address found from I2C scanner
#define RS_pin    0                  // pin configuration for LCM1602 interface module
#define RW_pin    1
#define EN_pin    2
#define BACK_pin  3
#define D4_pin    4
#define D5_pin    5
#define D6_pin    6
#define D7_pin    7

LiquidCrystal_I2C lcd(I2C_ADDR, EN_pin, RW_pin, RS_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACK_pin, POSITIVE);
//Pins for the LCD are SCL A5, SDA A4

int out_flag = 0;

const int TagCount = 1;
char *tagValues[] = { "982_000175197588" };
char *tagNames[] = { "0800 5" };
char rfidData[16];
int readCount = 0;

void setup()
{
  Serial.begin(9600);
  lcd.begin(20, 4); // set up the LCD's number of columns and rows:
  RFID.listen();
  Serial.println("Tag number will be displayed here if detected by the module:\n");
  lcd.print("Your tag No is:");
  lcd.setCursor(0, 1); //place cursor on 2nd row for tag number
  
  RFID.begin(9600); // set the data rate for the RFID reader
  delay(10);

}

void loop()
{
  while (RFID.available()) {
    byte C = RFID.read();
    rfidData[readCount] = C;
    readCount++;
    out_flag = 1;
  }
  if (out_flag > 0) {
    Serial.println();
    out_flag = 0;
    lcd.setCursor(0, 1);
  }
  
  for(byte t=0; t<TagCount; t++)
   {
      if(strcmp(rfidData, tagValues[t]) == 0) // If there is a match
      {
         Serial.print(tagNames[t]);
         lcd.write(tagNames[t]);
         break; // No need to test the other values after finding a match
      }
   }
  readCount = 0;
}

Any closer this time :confused:

Cheers.

So is this the number of tags I have stored? 0 being one tag, 1 being two?

You are mixing up count and index. The count of items in the arrays is 1. The index of the only item in the arrays is 0.

Any closer this time

Yes, but not quite close enough. rfidData is a char array. It is NOT, however, a string, and strcmp() expects a string. After:

    rfidData[readCount] = C;
    readCount++;

Add:

    rfidData[readCount] = '\0';

That adds a NULL terminator, and makes rfidData a string, and strcmp() will work.

That is, IF all the rfid data arrives while the while loop is reading it, which may not be the case.

Add:

    Serial.print("rfidData: [");
    Serial.print(rfidData);
    Serial.println("]");

after the end of the while loop. We need to see if all the data arrives "at the same time". If not, we need to see whether there is anything that indicates when the record is complete.

PaulS thank you so much, its starting to come together. I now know what you mean about data arriving at the same time. When I put the Serial.print code in after the while loop, sometimes I got the whole number and sometimes only part of it. I added a 5 millisecond delay in the while loop which seems to have fixed that. Is this ok?

I left rfidData as:

char rfidData[16];

or should it be, with the added NULL values:

char rfidData[32];

This is the code now with two tags.

#include <Wire.h> 
#include <LCD.h>                     // Standard lcd library
#include <LiquidCrystal_I2C.h> 
#include <SoftwareSerial.h>

SoftwareSerial RFID(2, 3); //pin2 Rx, pin3 Tx

#define I2C_ADDR  0x27               // address found from I2C scanner
#define RS_pin    0                  // pin configuration for LCM1602 interface module
#define RW_pin    1
#define EN_pin    2
#define BACK_pin  3
#define D4_pin    4
#define D5_pin    5
#define D6_pin    6
#define D7_pin    7

LiquidCrystal_I2C lcd(I2C_ADDR, EN_pin, RW_pin, RS_pin, D4_pin, D5_pin, D6_pin, D7_pin, BACK_pin, POSITIVE);
//Pins for the LCD are SCL A5, SDA A4

int out_flag = 0;

const int TagCount = 2;
char *tagValues[] = { "982_000175197588","982_000175197671" };
char *tagNames[] = { "0800 5","0801 6" };
char rfidData[16];
int readCount = 0;

void setup()
{
  Serial.begin(9600);
  lcd.begin(20, 4); // set up the LCD's number of columns and rows:
  RFID.listen();
  Serial.println("Tag number will be displayed here if detected by the module:\n");
  lcd.print("Your tag No is:");
  lcd.setCursor(0, 1); //place cursor on 2nd row for tag number
  
  RFID.begin(9600); // set the data rate for the RFID reader
  delay(10);

}

void loop()
{
  while (RFID.available()) {
    byte C = RFID.read();
    rfidData[readCount] = C;
    readCount++;
    rfidData[readCount] = '\0';
    out_flag = 1;
    delay(5);
  }

  if (out_flag > 0) {
    Serial.println();
    out_flag = 0;
    lcd.setCursor(0, 1);
  }
  
  for(byte t=0; t<TagCount; t++)
   {
      if(strcmp(rfidData, tagValues[t]) == 0) // If there is a match
      {
         lcd.setCursor(0, 1); //place cursor on 2nd row for tag number
         Serial.print(tagNames[t]);
         lcd.write(tagNames[t]);
         break; // No need to test the other values after finding a match
      }
   }
  readCount = 0;
}

Does it look ok now?

How hard would it be to do the same thing as tagValues, and tagNames is doing, but in two colums in a text file on an sd? It would make it easier to add and remove records.

But I'm over the moon with this so far.

Again thank you,

Damien.

Is this ok?

It's not the best solution, but if it works, you can leave it for now.

or should it be, with the added NULL values:

You only add one NULL. Notice that readCount is not incremented after adding the NULL. The array size should be 17, to hold the 16 character and the NULL. Personally, I like even numbers, so I'd make the array size 20.

I'd also make sure that readCount was less than 20 before writing to the array.

How hard would it be to do the same thing as tagValues, and tagNames is doing, but in two colums in a text file on an sd? It would make it easier to add and remove records.

Reading the data from the SD card is not that hard. There is an example, DumpFile, in the SD library that shows how to read a file.

It does not deal with saving the data in an array, but you know how to do that (it's the same as saving serial data in an array). It also does not deal with individual records in a file, but that's simply a matter of checking the character before adding it to the array. If it is a carriage return or line feed ('\n' or '\r'), parse the saved data (strtok()) to get the name and value. Otherwise, add the character, increment the index, and add the NULL.

When you have a name and value, you can compare that to the tag value you have. If they match, use the name, close the file, and break out of the while loop that is reading the file.

Cheers I'll have a look into that.

I left the array size at 16. It seems to work ok. If I changed it to 17 or 20, I lost the display of the visual tag on the lcd. It might not be pretty but it works.

Cheers for all your help. I'll rest my brain for a bit before having a look at the sd card stuff :slight_smile:

Thanks,

Damien.