Introduction
For our farm we have the sheep tagged with electronic eartags. These conform to ISO 11784 and ISO 11785, with Full Duplex (FDX/FDX-B) communication. The Wikipedia page on the matter is very good, as is the priority1design on the protocol.
As I wanted to mess around with some code around these earmarks, I needed hardware to actually read these tags. The important bit here is that these tags are 134.2Khz based and not the more common 124KHz RFID tags, and thus those readers cannot be used. The one advantage is that the only remaining found boards usually seem to be fairly high quality. I picked a Jedrek board as I could find at least some reasonable documentation on them. The board I picked pushes the data out via serial and thus I also picked up a cheapo UART Serial to USB converter.
The hardware
- A Jedrek based board (not listed on their website) "134.2KHz HDX FDX-B ISO/IEC 11784/5 Animal Tag reader Module RS232 TTL Interface"
- UART to USB converter for serial
The setup
I've 3D printed a little antenna holder, such that there is no stress on the small wires to the antenna PCB. I've got three wires from the UART board to VCC, GND and TXD (of course, RXD on the UART board). Some attention when inserting the black PCB onto the antenna board is required, as there is no good indication of the pins on the board. However, looking at the bottom, seeing what pin is GND and which one is VCC is a good way to make sure you're powering the correct pins.
Decyphering the protocol
If you've read the Wikipedia page and the priority1design page on the protocol, you might reasonably expect the board to output some nice bitstrings that conform to the protocol. However, for some reason the board embeds the data in some sort of odd ASCII padded data. This might have something to do with the BDP transmission system, but I do not know enough about electronics to figure it out.
unfortunately, decyphering how they get from the given ASCII to the ISO contents isn't actually that clear in the Jedrek documentation. Thus, I found this blog post to be warranted.
Step 1: Getting the data
The first step is reading some data from the board. As I didn't know how exactly the data would be tagged coming from the board I did some repeat
reads until I got a repeating pattern which I could figure out.
All the code here is Python 3.
For the serial connection I used the pyserial
module
import serial
ser = serial.Serial('COM3', stopbits=2)
for _ in range(4):
ser.read(32)
Step 2: ASCII shenanigns
The final data, adjusted for the 02 start and 03 end that the documentation calls for, resulted in b'\x020B07AA3571010120010000000000w\x88\x03'
We take that as the base for the rest of the operation. However, to do actual meaning full operations on the ASCII data, this first has to be converted to an
easier to work with binary format. To that end I used the bitstring
module.
tag = BitArray(bytes=ser.read(32))
base = tag.hex
'023131383039433943313730303745333030303030303030303030303005fa03'
Stripping the 02 start padding and the last 6 characters which contain the checksum etc, we get the payload:
core = base[2:-6]
The core: '31313830394339433137303037453330303030303030303030303030'
Now this is where the ascii embedding really becomes visible, the 03's infront of nearly every byte. To convert this to a meaning full number we have to take each of the ascii chars and convert them to decimal, via a binary step. What I mean by this is, we firstly take that long hex string and split it up into ascii chars again:
core = [str(chr(int(x, 16))) for x in core]
Now, for just the country tag, we take these bytes:
countrystr = core[10:10+3]
This is then a set of three ASCII chars, which we take the binary data from to get the actual number. One important bit here is that we only care about the lower 4 bits for every ascii char. Additionality, the order is reversed. So, to get the actual number we finally do:
binarray = [BitArray(uint=int(x, 16), length=4).bin for x in countrystr]
binary = "".join(reversed(binarray))
num = BitArray(bin=binary).int
The final result
Taking all three of these steps together I've built a small Python script which very simply reads from a serial port and prints the country number and tag number. Two small additions are that I read in a continious non blocking fashion for better responsiveness and that I find the correct bitstring in what is read by using a very basic regex pattern
import serial
import argparse
from time import sleep
from bitstring import BitArray
import re
pattern = r'.*(02.{56}03).*'
pattern = re.compile(pattern)
def extract_num(readarray):
"""
Find occurances of numbers, remove them from the bit array
Return the new bitarray, foundtag or none
"""
readstring = readarray.hex
result = re.match(pattern, readstring)
if result != None:
hextag = '0x' + result.group(1)
# Remove any pre bullshit data
res = list(readarray.split(hextag, count=2))[1]
# And remove the found tag
res.replace(hextag, '')
return res, BitArray(hex=hextag)
else:
return readarray, None
def parse_tag(tag):
base = tag.hex
# Split it in ascii chars
core = [(base[i:i+2]) for i in range(0, len(base), 2)]
core = core[1:-3]
# Now convert to ascii
core = [str(chr(int(x, 16))) for x in core]
animalstr = core[:10]
countrystr = core[10:10+3]
countrynum = parse_asciitag(countrystr)
animalnum = parse_asciitag(animalstr)
print("Country {}, Tag: {}".format(countrynum, animalnum))
def parse_asciitag(asciistr):
binarray = [BitArray(uint=int(x, 16), length=4).bin for x in asciistr]
binary = "".join(reversed(binarray))
num = BitArray(bin=binary).int
return num
if __name__ == "__main__":
serdev = 'COM3'
readarray = BitArray()
# zero time out, non blocking
with serial.Serial(serdev, stopbits=2, timeout=0) as ser:
while True:
readres = ser.read(32)
readarray.append(BitArray(bytes=readres))
readarray, tag = extract_num(readarray)
if tag is not None:
print("Found tag {}".format(tag))
parse_tag(tag)
sleep(1)
References
http://www.jedreksys.com/download/134-K-Animal-Tag-FDX-B-ISO11784-Reader-Module.html https://en.wikipedia.org/wiki/ISO_11784_and_ISO_11785 http://www.maxmicrochip.com/ISO_types.htm http://www.priority1design.com.au/fdx-b_animal_identification_protocol.html https://en.wikipedia.org/wiki/ISO_3166-1