RFID Reader with serial interface

First of all I need to say this was the first python project I've ever scripted. A reorg of the code will be done i time but for now this should work out of the box. The D302 RFID Reader is connected with USB to the computer and emulates a serial interface [ /dev/ttyUSB0 or COMx ].

Thanks to some very good reverse engineers on the world wide web I figured out how to communicate with this serial protocoll to read and write to RFID Cards. I decided not to post and comment the full code here better to link the references to the full repo on github. What I want to point out here is the use of Qt-Designer and pyInstaller to build multiplatform packages.


SerialOpen

In this code snippet I just want to show you the way of opening a serial port. In our case it was important to set the bautrate to 4800 and the xonxoff to 0. The port (ie /dev/ttyUSB0) we do get as an argument. On the end of the script there is a def called serial_ports which checks the operating system on startup and offers the available ports in the Qt GUI.

The initial_message() is very important because if this fails the script will throw an exception. This part is quite trivial because this is standard serial communication.

d302/source/serial_d302.py

 def open_port(self, port):
        print(port)
        try:
            self.ser.baudrate = 4800
            self.ser.xonxoff = 0
            #self.ser.rtscts = False
            self.ser.bytesize = serial.EIGHTBITS
            self.ser.timeout = 1
            self.ser.write_timeout = 2
            #self.ser.dsrdtr = True
            #self.ser.rtscts = True
            self.ser.port = str(port)
            self.ser.open()
            self.initial_message()
            return True

        except:
            self.initial_message()
            return False

init - read -write

As I mentioned before it is senseless to comment the full code because this would end unreadable so I point out the important parts of

  • def inital_message(self): [L60]
  • def read_message(self): [L74]
  • def write_message(self, hex, lock=False): [88]
initString = bytearray([0x02, 0x01, 0x00, 0x00, 0x03, 0x03])

The D302 Reader needs a HELLO to know that we want to talk to him. I forgot the source of the reverse engineer who found out all these strings but with some google you will find some sources.

writeString = bytearray([0x02, 0x01, 0xa4, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x03])

I'm sorry for some users who might be irritated in the use of varname. Actually this should be named "readString" but I wanted to say this is the bytarray we need to send to put the reader into READING MODE. The Answer of the reader will also be in HEX so we have to hexlify it [ answerString ]

    def write_message(self, hex, lock=False):
        self.initial_message()
        
        if lock: 
            lockbit = 'ff' 
        else: 
            lockbit = '00'
            
        time.sleep(1)
        writeSeq = '0201a50b0000000000'
        lockSeq = lockbit
        checksumInt = self.calc_checksum(writeSeq+lockbit+hex)
        intList = self.int_list(writeSeq+lockSeq+hex)
        writeString = bytearray(intList)+bytearray([checksumInt, 0x03])
        self.ser.write(writeString)
        time.sleep(1)
        self.ser.flushInput()
        return hex+str(checksumInt).zfill(2)

Here I need to say little bit more. For sure the write command needs a sequence to tell the reader which mode he should switch to. But unlike the init and read we also have to write something to the card. Therefor we also need to verify that the data we are sending is valid!

 

lockbit


If you have already experience with RFID Cards you might know that you can lock them ( not reversable ). The lockbit written to the card tells every reader that the card is in readonly mode. Since you cannot reverse this bit to 0 the card can never be rewritten with any other Infos

writeSeq


The writeSeq is the preData string wich tells the Reader that we want to write data to the card. Together with the lockbit + the hex string given as argument we

checksumInt


is the checksum of the string containing (writeSeq+lockbit+hex).

writeString


If it looks a bit confusing I tried to create long strings with all hexdata served as <chars> and make bytearrays out of them in the end.

self.ser.write(writeString)


This is the final write command to the serial opened port.

last but not least


I need to say that I should have declare all vars with its datatypes but <hey> this was my first code and python3 was in version 3.[low] at this time. So for the future releases there is a lot to be done.