Wie im Post 'Ein RFID Reader für 125Hz' bereits erwähnt, möchte ich gerne ein Lesegerät für RFID Transponder bauen, die für die Kennzeichnung von Tieren verwendet werden. Die Anregung dieser Transponder erfolgt in der Regel bei 134 kHz und es wird ein ein Protokoll nach ISO 11784 & 11785 verwendet.
Ich habe mir daher eine einfachen Logik Analyzer und ein RDM6300 RFID Modul für Arduino und Raspberry Pi besorgt. Der Schaltung des Modules sieht wie folgt aus.
Eine Messung ergibt, dass der von Controller C8051 an Pin 1 ausgegebene Erregertakt ca. 129 kHz beträgt
Sie liegt damit zwischen 125kHz und 134kHz. Am Pin 7 des OpAmp LM358 kann das digital aufbereitete Empfangssignal abgegriffen werden. Ich habe ein Kabel angelötet, und versucht über den Analyzer einen Tag für Katzenhalsbänder auszulesen.
Überraschenderweise ist auch der Halsband Chip EM4100 codiert
Ein richtiger Glastag zur subkutanen Injektion lässt sich allerdings nicht auslesen.
Der EM4100 Decoder liefert unsinnige Werte. Das Protokoll nach ISO 11784 & 11785 wird nicht unterstützt. Ich bin daher der Idee verfallen den Datenstrom durch ein Python Programm zu dekodieren.
Folgenden Aufbau für ein Lesegerät habe ich realisiert. Er besteht aus einem Raspberry Zero W, einem OLED Display, dem Logic Analyzer und dem RDM6300 Modul.
Das OLED Display mit einer Diagonalen von 0,96 Zoll hat eine Auflösung von 128 x 64 Pixel. Zur Ansteuerung steht eine I²C Schnittstelle mit dem Standard-Controller SSD 1306 zur Verfügung. Es wird entsprechend mit den SDA und SCL Datenpins, sowie VCC und GND des Raspi verbunden.
Folgende Systemprogramme werden benötigt.
'sudo apt-get install -y python-dev python3-dev python-imaging python-smbus i2c-tools git python3-pip python-setuptools build-essential git-core libi2c-dev i2c-tools lm-sensors python-pip'
Dann müsen noch einige Bibiotheken installiert werden.
'git clone git://github.com/rm-hull/ssd1306.git
cd ssd1306
sudo python setup.py install
cd examples
git clone https://github.com/rm-hull/luma.examples.git
cd luma.examples/examples'
und schon kann man sich einige mitgelieferte Beispiele anschauen. Für animierten Text und Logo z.B. python crawl.py --display ssd1306 aufrufen.
Um den Logik Analyzer unter Raspian (Raspberry Pi OS 5.10) zum Laufen zu bringen, sind keine zusätzlichen Treiber nötig. Nach
'sudo apt-get install pulseview
sudo apt-get install sigrok'
kann die graphische Bedienungsumgebung PulseView unter Entwicklung/PulseView auf dem Desktop aufgerufen werden. In PulseView muss u.U. 'Saleae Logic' ausgewählt werden.
Um Daten automatisch aufzuzeichnen bietet sich die Verwendung von siglok-cli an. Um z.B. 200ms lang auf D0 in eine csv-Datei ohne header zu schreiben ruft man
'sigrok-cli --config samplerate=50000 --samples 10000 --channels D0 -o /home/pi/RFID/example.csv -O csv:dedup:header=false'
auf.
Noch ein paar Worte zum Setup des Raspi Zero. Für die einfachere Administration empfiehlt es sich einen Zugang als root einzurichten. Das Passwort wird mit 'sudo passwd root' gesetzt. Nachdem xrdp installiert ist, kann man sich per remote desktop Verbindung einwählen. Damit jedoch im remote desktop die task bar korrekt angezeigt wird sollte in 'root/.config/lxpanel/LXDE-pi/panels' der Eintrag
Plugin {
type=volumepulse
Config {
}
}
entfernt werden.
Um ein sauberes Herunterfahren auch ohne einloggen zu gewährleisten, habe ich zwischen GPIO4=Pin7 und GND=Pin9 einen Taster geschalten. Durch den Eintrag
'dtoverlay=gpio-shutdown,gpio_pin=4, active_low=1,gpio_pull=up'
in der '/boot/config.txt' fährt der Raspi dann auf Kopfdruck herunter. Ein schöner Nebeneffekt dabei ist, dass die Anzeige des OLED Displays dabei erhalten bleibt und z.B. weiterhin die ausgelesene ID des Transponders anzeigt.
Damit unser Phyton Programm zum Auslesen des Tags später automatisch beim booten ausgeführt wird, muss man in '/etc/rc.local' vor 'exit 0'
'python /home/pi/RFID/tag_reader7.py &'
eintragen. Das '&' Zeichen gewährleistet dass 'tag_reader7' im Hintergrund startet, und die weitere Boot Sequenz nicht beeinträchtigt wird. Optional kann dann später, wenn man sich in den Raspi einloggt, Python im Taskmanager gestoppt, beendet oder gekillt werden.
Die eigentliche Software des Lesegerätes macht im Wesentlichen folgendes. Nach dem Laden des wird zunächst ein kleines Bildchen angezeigt
Dann werden 5k Datenpunkte mit 50k Abtastrate in einem Textfile aufgezeichnet. Wird keine '00000000001' header Folge gefunden, wird 'No header detected' angezeigt. Wird ein header gefunden, stimmt der tag aber nicht mit dem nachfolgenden tag überein wird 'ID Error' ausgegeben. Stimmt Alles werden 'Country Code' und 'ID' ausgewertet und angezeigt. Fehlermeldungen und ausgelesene IDs werden zusätzlich in einer einfachen Logdatei gespeichert.
Nach den ersten Funktionstests habe ich die gesamte Elektronik in ein selbst gedrucktes Gehäuse gepackt.
Der gesamte Reader sieht dann so aus
Die 5V Stromversorgung erfolgt aus einer kleinen Powerbank. Zusätzlich habe ich noch einen kleinen An/aus Schalter eingebaut.
Wie im Folgenden Bild zu sehen kann damit nun ein kleiner subkutaner Transponder ausgelesen werden. Der RFID Transponder ist im Hintergrund zu erkennen.
Leider beschränkt sich die Reichweite auf wenige Millimeter. Die Anregung mit 129 kHz durch das RDM6300 Modul ist offenbar nicht ideal. Weitergehende Optimierung der 'Antenne' wäre nötig. Auch die relativ lange Boot Zeit des Raspi von ca. 30 Sekunden ist störend. Die Verwendung eines Micro Controllers z.B. eines Arduinos ist hier von Vorteil. Nichtsdestrotrotz zeigt der Aufbau das Funktionsprinzip sehr schön wie ich finde.
Viel Spaß beim Nachbauen und selber Experimentieren...
Abschließend noch das gesamte PythonSkript
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1306
import time
from PIL import ImageFont
import os.path
from PIL import Image
import re
import array as arr
import sys
from bitarray import bitarray
from datetime import datetime
def str2bool(v):
return v == "1"
serial = i2c(port=1, address=0x3C)
device = ssd1306(serial, rotate=2)
def show_logo():
img_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'images', 'FX-B_reader.png'))
logo = Image.open(img_path)
#show logo
with canvas(device) as draw:
draw.bitmap((20, 0), logo, fill="white")
draw.text((0, 40), "presented by..."+'\n'+"allaboutplainwhite", fill="white")
time.sleep(3)
def loop():
#read tag
#5k samles at 50k rate and save as text file, 1 bit per line only
os.system("sigrok-cli --config samplerate=50000 --samples 5000 --channels D0=In -o /home/pi/RFID/bit_session.bin -O bits:width=1")
#decode tag
#read analyzer data
c = bitarray() #bitarray to store analyzer data
with open('/home/pi/RFID/bit_session.bin', 'r') as fh:
next(fh) #skip header
next(fh)
for line in fh:
c.append(str2bool(re.sub(r'[^0-1]', '', str(line)))) #remove everything beside 0 and 1 convert to bool and write to bitaray
#decode biphase encoding
times = arr.array('i',[]) #array of int to store ellapsed time since last signal change (signal edge)
edge=0 #position of last signal change
i=0
for i in range(len(c)-1): #walk through analyzer data
if c[i] != c[i+1]: #if signal change detected
times.append(i+1-edge) #note down ellapsed time since last change
edge = i+1
i=0
tags=bitarray() #array to store decoded data for all tags
while i in range(len(times)-1): #decode information acc to FX-B protocol
if times[i] > 7 : #if long time since last change (> 32*field cycle time*sampling rate *1/2)
tags.append(1) #note down tag bit 1
i=i+1
elif times[i+1] < 12 : #else if short time and next entry also short (< 32*field cycle time*sampling rate *2/2)
tags.append(0) #note down tag bit 0
i=i+2
else: i=i+1
#calculate tags
header=bitarray('00000000001')
header_flag=False
message="No header detected"
for i in range(len(tags)-2*128): #find header
if tags[i:i+11]==header:
header_flag=True
break
if header_flag: #proceed if header detected
tag1=tags[i+11:i+128] #extract tag1 without header
tag2=tags[i+128+11:i+2*128] #extract tag2 without header
if tag1==tag2:
del tag1[8::9] #remove seperator bit
ID1=bitarray(tag1[0:38]) #extract reverse ID (LSB first)
j=0
while j<19: #rotate ID
b=ID1[j]
ID1[j]=ID1[37-j]
ID1[37-j]=b
j=j+1
#print("ID:",int(ID1.to01(),2)) #convert to decimal and print
ID=int(ID1.to01(),2)
#ID_str= "ID:" + str(ID)
CC1=bitarray(tag1[38:48]) #extract lsb first Country Code
j=0
while j<5: #rotate Country Code
b=CC1[j]
CC1[j]=CC1[9-j]
CC1[9-j]=b
j=j+1
#print("Country Code:",int(CC1.to01(),2)) #convert to decimal and print
Country=int(CC1.to01(),2)
message = "Country Code:" + str(Country) +'\n' + "ID:" + str(ID)
else:
message="ID Error"
#show tag
#print(message)
with canvas(device) as draw:
draw.text((0, 0), message, fill="white")
#time.sleep(1)
datei = open('/home/pi/RFID/logfile.txt','a') #save message in logfile
now = datetime.now() #get current date and time
dt_string = now.strftime("%d/%m/%Y %H:%M:%S ") # dd/mm/YY H:M:S
datei.write("\r\n" + dt_string + message)
#main
show_logo()
while True:
loop()
Keine Kommentare:
Kommentar veröffentlichen