Sunspot Home

RFID chicken name detector - working well - NB! see notes at bottom of page!

These are my backup project notes - please email me if interested

or you may like to debate the Chicken RFID issues here ->

rfid brickrfidkeyfob

Detect my chickens by "name" when they visit the nest boxes.
Build a web page that reports which lay the most (and least!) eggs.
Store a webcam photo of the detected laying bird.

Later - detect all visits to the feeder to ensure that any bird that does not seem
to have visited a nest box does indeed have a working RFID tag.

I purchased 2-off 125KHz RFID detector boards for about £20 including postage directly from Seeedstudio
Google for ELB149C5M to find many sites that stock this board.

This feeds data to an nslu2 (Slug) running Linux image nslu2_51_110124_image.bin on internal memory alone - normally used for HD webcams as here

(NB there is no need to have an Arduino - just the module above and a device to read the RS232)

I would like to find some 125KHz RFID chicken leg rings (they are sold wholesale - please email me if you find a retail source)
At present I am using the common (and cheap) ready programmed key fob tags from eBay (picture above) fastened with a nylon tie (see below).
In the UK 10 of these can be bought for £1.75 total including postage from China - how do they do that ?!


When a chicken is in the nest box I get 14 or more detections of its code an the RFID card per minute - even if they seem to sit still.
And a bird will often sit for 30 to 60 minutes.

RFID Module RS232
The RFID card RS232 is TTL level (the output terminal is normally held at 5V - data pulses the line to zero volts)

Combine RS232 from several modules
At present I have 3 nest boxes and 3 detector modules but may build more.
I am planning to have a sensor on the feeder and perhaps one at the exit to the outdoor compound to prove that the chickens that do not lay still have a working RFID leg-fob.

From chips in my stock I built a (crude) 6 input logic combiner.
All inputs are normally high and so is the output. A negative pulse on any input will appear at the output.
The CMOS runs at 3.3 volts obtained from the Slug. The CMOS can handle the 5 volt input pulses from the RFID cards directly.

(Note - since a laying chicken gives many detector outputs it might have been possible to multiplex several detector coils to one detector)

TTL-RS232 combiner circuit - NB! viewed from the solder side!


The detector coil
The small coil that comes with the kit is not big enough for this job.
I wound a coil on a circular plywood former of
6 inches diameter sandwiched between two square plywood boards as large as the nest box base
I tuned it on a 'scope and found it needed about 41 turns of wire.
I used standard plastic covered solid core connecting wire since there is plenty of room and I had a big reel in stock from an electronics sale.

The scope saw 42 volts peak to peak on the coil - more than the 38 volts from the small coil.
I think the Q was so high that I could have tuned it to give more but I believe that may have caused the signal to be more than the chip could handle.
At up to 5 cm distance the coil detects a horizontal key fob tag at the centre and a vertical tag over the windings.

Just the job for the random orientations of the chicken's leg while laying!

Code Overview - if you are new to all this please start at my home page

The nslu2 (Slug) detects the TTL level RS232 on it's serial port.
A bash script saves the raw data strings to a folder that is in RAM (to save the FLASH memory).

A Joggler on the home LAN runs a Blassic basic program to process the data.
It includes a bash code segment that uses wget to fetch the data from the ramdisk in the Slug website.
Canvas (OK in Firefox) displays the time graph on a web page

Slug code

Silence the terminal chatter

The Slug serial port normally outputs data from the Linux kernel.
To remove this "terminal chatter" after the boot messages modify /etc/inittab to -

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K stop
# tts/0::askfirst:/bin/ash --login
# ttyS0::askfirst:/bin/ash --login
# tty1::askfirst:/bin/ash --login

Raw mode
It is necessary to change the baud rate to 9600 and to place the port in "raw" mode
(it then accepts characters one by one - no need to press RETURN at the end of a line)

To do this you need to have stty (part of busybox) loaded - this has to be done when the kernel is compiled
- I recompiled to add stty then simply replaced busybox on my slug to avoid setting it up all over again
- but then you need to let the slug know how to find stty

cd /bin
ln -s /bin/busybox stty

Then do

stty raw ispeed 9600 ospeed 9600 -F /dev/ttyS0

see if it worked with

stty speed -F /dev/ttyS0

Then connect the RFID module to the Slug serial port as above
and in a terminal a session type

cat /dev/ttyS0

Then when a tag is detected by the coil you see something like


Some early code
- see the bottom of the page for the latest updated set of files

I save the raw data to ramdisk (mounted as a folder under /www) on a Slug.
I wget that data using a Joggler also on the home LAN.

The bash script on the Slug


# /www/RFID/save_ID_to_ramdisk.shs

tty raw ispeed 9600 ospeed 9600 -F /dev/ttyS0
# loop forever
while true
rm -f /var/www/ramdisk/RFID_raw.txt

cat < /dev/ttyS0 > /www/ramdisk/RFID_raw.txt &

sleep 5
kill $catPid

RFID_raw=`cat /www/ramdisk/RFID_raw.txt`

echo $RFID_raw
echo starting another loop

The above script ran on one Slug but not on two others with identical firmware.
I could send data to a ZTerm terminal on the iMac but not receive data on the Slug.

I loaded minicom onto the Slug and that would send data both ways.
After minicom was removed (kill -9 `pidof minicom`)
the code above worked on all three Slugs.

So how to start then stop minicom from a bash script before running ?
This works -

# /www/RFID/

minicom > /dev/null 2>&1 &

sleep 10

kill -9 `pidof minicom`

null 2>&1 sends the output screen from minicom to a dump and & allows the script to proceed to the next line (thank you Google!)
(There must be better ways to do this (?) but it runs)

The Blassic basic on the Joggler to work on the data


I see many null characters and then the detected code at the end.

Blassic basic to the rescue

blassic /var/www/RFID/Read_RFID.bas grabs the raw data using

wget -O /var/www/ramdisk/RFID_raw.txt

it saves all detections to
(for research - remove this later)

It saves lines of html code for Canvas to create a time plot of the laying in

Having built the html lines it runs


which stitches together a graphic web page

(Read_RFID.bas and the notes below were updated on 13/1/2013)

#! /usr/sbin/blassic

' /var/www/RFID/Read_RFID.bas

DIM chicken$(22,720) '22 chickens and lines on the plot, 720 pixels across the plot for 24h
DIM chicken_record$(22)
GOSUB ChickenArraysToZero
SHELL "rm /var/www/ramdisk/Pixel_Records.txt"

LABEL StartAgain
'TIME$ is hh-mm-ss - get it in pixel format - 0 to 720 for 24 hours
mytime$ = TIME$
hour$ = LEFT$(mytime$, 2): minute$ = MID$(mytime$, 4, 2)
hour = VAL(hour$) : minute = VAL(minute$)
pixel_minute = ROUND (minute/2) '(30 pixels per hour) - 1/2 and 2/2 give 1, 3/2 and 4/2 give 2 etc
pixel_time = hour*30 + pixel_minute
PRINT "pixel_time = ", pixel_time

IF pixel_time = 30 THEN GOSUB ChickenArrayToZero 'empty the chicken$ array at 1 am

IF pixel_time = 699 THEN j = 0
IF (pixel_time = 700) AND (j = 0) THEN GOSUB StoreBirdHistories 'at the end of the day store each birds performance
ChickenName$ = "no name yet"
RFID1$ = "MT"

PAUSE 2000

'Grab the RFID data from the Slug

SHELL "wget -O /var/www/ramdisk/RFID_raw.txt"
PAUSE 1000
OPEN "/var/www/ramdisk/RFID_raw.txt" FOR INPUT AS #1
IF EOF (1) THEN GOTO StartAgain
INPUT #1,RFID_long_string$
' print the full string from the RFID detector
PRINT "RFID_long_string$ is ", RFID_long_string$

' there are lots of null characters before the data string - the real data is at the very end
StringLength = LEN(RFID_long_string$)
PRINT "total string length", StringLength
' find the last 3 strings in the stream (there may be 1 or 2 detections - even 3 at times)
RFID1$ = MID$ (RFID_long_string$, (StringLength - 12), 12) : PRINT "right of 3 captures", RFID1$
'RFID2$ = MID$ (RFID_long_string$, (StringLength - 26), 12) ' PRINT "middle of 3 captures", RFID2$
'RFID3$ = MID$ (RFID_long_string$, (StringLength - 40), 12) ' PRINT "left of 3 captures", RFID3$
' the last one seems to be plenty - they stay in the box for a long time!

' get decimal value of RFID tag for this cycle
hex1$ = MID$ (RFID1$, 5, 6)
PRINT 'hex1$ = ', hex1$
tagDecimal = VAL ("&h"+hex1$)
IF tagDecimal = 0 THEN GOTO StartAgain 'no detection this cycle
tagString$ = STR$(tagDecimal)
PRINT "Hex 1 = ", hex1$
PRINT "Decimal as on tag = ", tagString$
LastTwo$ = RIGHT$(tagString$, 2)
LastThree$ = RIGHT$(tagString$, 3)
PRINT "last 2 numbers", LastTwo$,"last 3 numbers", LastThree$

GOSUB GetChickenPixelLine 'from the last three numbers of the RFID identifier find the pixel line to plot the data
PRINT "ChickenNumber ", ChickenNumber, "pixel_time ", pixel_time
PRINT "chicken$(ChickenNumber,pixel_time) is ", chicken$(ChickenNumber,pixel_time)

IF chicken$(ChickenNumber,pixel_time) = "0" THEN chicken$(ChickenNumber,pixel_time) = "1" : GOSUB StoreNewDetection
' all chicken$() are 0 at reboot and day start (1 a.m.) - in any 2 minute pixel time period chicken$() will become 1 if there is a detection
' so on the first detection in this 2 minute pixel period store a detection to print on the graph
' but if detected again in this period do nothing

' store all the detections as text
IF RFID1$ = "MT" THEN GOTO SkipPrint
OPEN "/var/www/RFID/RFID_text_store.txt" for APPEND as #1
print #1,DATE$, TIME$,pixel_time,RFID1$,tagDecimal,LastThree$,ChickenName$
close #1

LABEL SkipPrint



PAUSE 1000
PRINT "Now running make_page.bas"
SHELL "/var/www/RFID/Build_RFID_webpage/make_page.bas"

GOTO StartAgain

LABEL ErrorNote
PRINT "Error"
GOTO StartAgain

LABEL GetChickenPixelLine 'modify this list as needed
IF LastThree$ = "261" THEN ChickenNumber = 1 : ChickenPixelLine$ = "8" : ChickenName$ = "1 YEL BLACK"
IF LastThree$ = "379" THEN ChickenNumber = 2 : ChickenPixelLine$ = "28": ChickenName$ = "2 YEL RED"
IF LastThree$ = "512" THEN ChickenNumber = 3 : ChickenPixelLine$ = "48": ChickenName$ = "3 YEL ORANGE"
IF LastThree$ = "641" THEN ChickenNumber = 4 : ChickenPixelLine$ = "68": ChickenName$ = "4 YEL YELLOW"
IF LastThree$ = "586" THEN ChickenNumber = 5 : ChickenPixelLine$ = "88": ChickenName$ = "5 YEL GREEN"
IF LastThree$ = "689" THEN ChickenNumber = 6 : ChickenPixelLine$ = "108": ChickenName$ = "6 YEL BLUE"
IF LastThree$ = "621" THEN ChickenNumber = 7 : ChickenPixelLine$ = "128": ChickenName$ = "7 YEL PURPLE"
IF LastThree$ = "329" THEN ChickenNumber = 8 : ChickenPixelLine$ = "148": ChickenName$ = "8 YEL WHITE8"
IF LastThree$ = "770" THEN ChickenNumber = 9 : ChickenPixelLine$ = "168": ChickenName$ = "9 BLU WHITE9"
IF LastThree$ = "097" THEN ChickenNumber = 10 : ChickenPixelLine$ = "188": ChickenName$ = "10 BLU LTBLU10"
IF LastThree$ = "604" THEN ChickenNumber = 11 : ChickenPixelLine$ = "208": ChickenName$ = "11 BLU BLACK"
IF LastThree$ = "125" THEN ChickenNumber = 12 : ChickenPixelLine$ = "228": ChickenName$ = "12 BLU RED"
IF LastThree$ = "931" THEN ChickenNumber = 13 : ChickenPixelLine$ = "248": ChickenName$ = "13 BLU ORANGE"
IF LastThree$ = "684" THEN ChickenNumber = 14 : ChickenPixelLine$ = "268": ChickenName$ = "14 BLU YELLOW"
IF LastThree$ = "547" THEN ChickenNumber = 15 : ChickenPixelLine$ = "288": ChickenName$ = "15 BLU GREEN"
IF LastThree$ = "525" THEN ChickenNumber = 16 : ChickenPixelLine$ = "308": ChickenName$ = "16 BLU BLUE"
IF LastThree$ = "282" THEN ChickenNumber = 17 : ChickenPixelLine$ = "328": ChickenName$ = "17 BLU PURPLE"
IF LastThree$ = "021" THEN ChickenNumber = 18 : ChickenPixelLine$ = "348": ChickenName$ = "18 BLU WHITE18"
IF LastThree$ = "396" THEN ChickenNumber = 19 : ChickenPixelLine$ = "368": ChickenName$ = "19 BLU WHITE19"
IF LastThree$ = "449" THEN ChickenNumber = 20 : ChickenPixelLine$ = "388": ChickenName$ = "20 449 "
IF LastThree$ = "485" THEN ChickenNumber = 21 : ChickenPixelLine$ = "408": ChickenName$ = "21 485"
IF LastThree$ = "833" THEN ChickenNumber = 22 : ChickenPixelLine$ = "428": ChickenName$ = "22 833"

'make the dots for the Canvas graphic
LABEL StoreNewDetection
OPEN "/var/www/ramdisk/Pixel_Records.txt" for APPEND as #1
PRINT #1,"jg_doc.drawLine(";STR$(pixel_time);",";ChickenPixelLine$;",";STR$(pixel_time+2);",";ChickenPixelLine$;");"
close #1
chicken_record$(ChickenNumber) = "1" 'record that it layed today
'GOSUB TalkNow

' announce the first visit today for this bird
SHELL "amixer -c 0 -- sset Master playback 70% > /dev/null 2>&1"
talk$ = "flite -t " + "' " + ChickenName$ + " " + LastThree$ + "'"
PRINT talk$
SHELL talk$

' build files of bird laying histories
LABEL StoreBirdHistories
j = 1 'j was set to 0 at time 699 - only come here once at time 700
FOR I = 1 to 22
File$ = "/var/www/RFID/ChickenRecords/ChickenRecord_" + STR$(I) + ".txt"
OPEN File$ for APPEND as #1
PRINT #1,DATE$,chicken_record$(I) 'if 1 then it layed today
close #1

' Zero the data for a new day
LABEL ChickenArraysToZero
FOR I = 0 TO 22
chicken_record$(I) = "0"
FOR J = 0 TO 720
chicken$(I,J) = "0"
SHELL "rm /var/www/ramdisk/Pixel_Records.txt"

Read_RFID.bas program notes - see numbers inset above

Slug attached to the RFID detectors at
cycles every 5 seconds and stores in the Slug at /www/ramdisk/RFID_raw.txt
data strings like
280011152509 280011152509 - there can be a few in the 5 second window
if the chicken is moving about

The Joggler in the office runs /var/www/RFID/Read_RFID.bas
which does the data processing to create the graph
It runs all the time and starts at boot time

Read_RFID.bas program flow

set data arrays for 22 chickens
clear these to zero
remove /var/www/ramdisk/Pixel_Records.txt
- Pixel_Records.txt will receive entries per line like jg_doc.drawLine(636,,638,);

2 [StartAgain]
find the pixel_time (0 to 720 on the X axis for 24 hours - 1 pixel = 2 minutes)

if it is 1am then empty the data arrays - chicken$(22,720) and chicken_record$(22)

if it is the end of the day (12 pm) store the record for each chicken in it's own file
at - /var/www/RFID/ChickenRecords/ChickenRecord_<number>.txt

grab the Slug's RFID data stored in
save locally in the Joggler at /var/www/ramdisk/RFID_raw.txt
If it contains no data (EOF) go to [StartAgain]

We now have the raw data -- RFID_long_string$
- just grab the last entry in the string since the bird gives many detections
this is 12 characters - - RFID1$ - RFID1$ was "MT" before the first detection

Work out the decimal tag number as printed on the tag
LastThree$ contains the last three decimal digits as printed - enough for us.

Go to SUB ChickenPixelLine to find
ChickenNumber ChickenPixelLine$ ChickenName$
(These have to be modified as needed - the chickens have a yellow or blue
key fob tag on one leg and a coloured ring on the other)

If chicken$(ChickenNumber,pixel_time) = 1 then we have a new detection for that chicken
- so store data like jg_doc.drawLine(375,128,377,128); for that chicken. This will
make a 2 minute long blob on the graph for that chicken at that pixeltime
StoreNewDetection saves these at /var/www/ramdisk/Pixel_Records.txt for the plot routine

If RFID1$ = "MT" go to SkipPrint then ask shell to run /var/www/RFID/Build_RFID_webpage/make_page.bas
(it must still be early morning - RFID1$="MT" was set at 1 am - and nothing has been detected yet)

If not then append historical data to /var/www/RFID/RFID_text_store.txt
DATE$, TIME$,pixel_time,RFID1$,tagDecimal,LastThree$,ChickenName$

check for errors then ask shell to run /var/www/RFID/Build_RFID_webpage/make_page.bas
This creates the new html to plot on any browser

GOTO StartAgain


make_page.bas - this is run from within the above program


' assemble a web page that plots RFID data
' view with (eg)

'the creation process is slow so save to a temp file then copy fast

'DATE$ is mm-dd-yyyy
mydate$ = DATE$
month$ = LEFT$(mydate$, 2): day$ = MID$(mydate$, 4, 2): year$ = RIGHT$(mydate$, 4)
date_number$ = year$+month$+day$
display_date$ = day$ + " " + month$ + " " + year$

'create the real file name for today
file_name_for_today$ = date_number$ + "_RFID_plot.html"

PRINT "file_name_for_today$ ", file_name_for_today$

' remove the previous save and dump error message
SHELL " rm /var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html > /dev/null 2>&1"

'top of page
SHELL "cat /var/www/RFID/Build_RFID_webpage/webpage_top.html >>/var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html"

'build the middle of the page from stored RFID readings - add the presence bars
SHELL "cat /var/www/ramdisk/Pixel_Records.txt >>/var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html"

'above bottom of page
SHELL "cat /var/www/RFID/Build_RFID_webpage/webpage_above_bottom.html >>/var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html"

'now finish the html
SHELL "cat /var/www/RFID/Build_RFID_webpage/webpage_bottom.html >>/var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html"

SHELL "cp /var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html /var/www/RFID/Build_RFID_webpage/"+file_name_for_today$

SHELL "cp /var/www/RFID/Build_RFID_webpage/temp_RFID_plot.html /var/www/RFID/Build_RFID_webpage/today_RFID_plot.html"




<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<title>RFID - Chicken nestbox visits</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<script type="text/javascript" src="wz_jsgraphics.js"></script>
<meta http-equiv="refresh" content="100" >

<div id="jg_docCanvas" style="position:relative;height:490px;width:800px;"></div>

<script type="text/javascript">
function myDrawFunction()
jg_doc.drawRect(0, 0, 720, 450);
jg_doc.fillRect(3, 3, 717, 447);






jg_doc.drawString("1 Light Sussex YELLOW 049",6,3);
jg_doc.drawString("2 white black specks BLUE 405",6,23);
jg_doc.drawString("3 Buff Orpington PINK 833",6,43);
jg_doc.drawString("4 All White GREEN 105",6,63);
jg_doc.drawString("5 Old Speckled GREEN 144",6,83);
jg_doc.drawString("6 White(ish) PURPLE 545",6,103);
jg_doc.drawString("7 Light Sussex PINK 250",6,123);
jg_doc.drawString("8 Buff Orpington PURPLE 021",6,143);
jg_doc.drawString("9 Buff Orpington YELLOW 776",6,163);
jg_doc.drawString("10 Big Black Ugly BLUE 407",6,183);
jg_doc.drawString("11 Red Hen YELLOW 236",6,203);




var jg_doc = new jsGraphics("jg_docCanvas");



The 19/1/2013 Results - Kitchen Joggler display
New "leg-tags" (see below) have been in use for 4 days now - all seems OK.
The black bar at the bottom shows time - it is about 15:30 and there were 9 eggs.

I aim to record the number of detections per bird each day - then estimate the eggs per week
It looks like "over 20 minute per day = 1 egg"


I noticed from the HD nest-box web cam (connected to the nslu2 that also reads the RFID boards) that if a bird just visits the nest box but does not sit there may or may not be a detection.
However, once they start to settle to lay the detections come every five seconds or so - and can do so for 30 minutes.


NB this failed - see lower down page for update

I have experimented with a new way of attaching the (cheap eBay) RFID key fobs to the chickens


The tags have indents at each side and 2 holes are drilled in each - I do not think that this lets water into the coil void.
A 9mm wide nylon tie is wired to the fob via the holes. The wires are tinned copper and are soldered on the side away from the chicken's leg.
(If the tie had been simply inserted into the hole in the end of the fob the point of the fob might have pressed on the leg.)
I was a bit concerned that the copper wire formed a shorted loop - but it is at a right angle to the detector loop and seems to have no bad effect.
The ties are tightened to a very loose fit round the chicken's ankle before the excess band is clipped off (The foot is much wider than the ankle).
The fobs are hollow and very light and birds show no sign of any discomfort. They happily layed eggs as usual on the day after the fitting!

A coloured band is attached to the other leg (I purchased some made from 10 different colours of plastic)

Below - Webcam view of the key-fob RFID tags in use, these are either of yellow or blue plastic - beware of some on eBay that are larger and thinner.

3fobs buff

I lifted the birds from their perches in the dark with the aid of a small torch wrapped in card so as to make a very small spot of light.
If a cloth is draped round the bird's head and upper body they lie still on a bench while the tags are fitted.

Comments? email me

"But this is where I always lay!"
"For goodness sake! - push girl, push!!
or - Play fair! I booked the 12:15 slot!




I used to clean the living quarters out every week - a disgusting job.

Now I keep about 5 inches of deep litter on the concrete floor.
I use oak leaves and any dry dead organics (like last years strawberry plants etc).
I add a new layer every two weeks or so. It seems to absorb the droppings as they fall.

It never smells and can be used as compost when the oak leaves fall again.
It looks like peat.


At the end of December 22 the birds were only laying 2 eggs a day.
I fitted a low power bulb in the living quarters that comes on for 14 hours a day.

Now (14th January) we get 8 to 12 eggs a day !! - (and they are wearing the RFID "leg fobs")


Subtle changes in the appearance of this Light Sussex are
definitely nothing to do with irradiation by the RFID detector coil.




Three in one box - White has just layed on top of Brown
White leaves - and then the egg falls and breaks

I have seen four birds in one box (and two boxes empty) and two more waiting to go in - bird brains?

The most recent set of files used in my Joggler are here - place them in the server web site root - eg in /var/www/RFID/

update 29/5/2013 - BEWARE some of the copper wire links to the RFID fobs are being pecked off - I will try small nylon ties next


13 March 2014

I fitted the birds with Chinese RFID tags like these :-
- but found that they had to be very much closer to the detection coils

I now use a 7.5 mm nylon tie round the birds ankle.
A smaller nylon tie (as wide as the hole in the key fob) joins the key fob (as above) to the larger tie.

rfid close

The fob is a bit exposed but they are very cheap and easy to replace.

The ties are loose and the birds seem to not notice them - I will be surprised if they can destroy the nylon.

It all seems to work now !