Card reader protocol
The contactless smartcard reader's protocol appears to be compatible with the CR802: http://www.chinareader.cn/datasheet/CR802.pdf
These are designed so you can have multiple card readers on a single bus communicating with a checksummed packetised protocol. Each reader has a "Node ID" which is included in each frame.
Multibyte attributes within the frames are stored in little-endian format (least significant byte first).
Request
A request frame sent too the reader consists of:
| Name | Length (octets) | Description |
| Start of frame | 2 | Always set to AA BB |
| Length | 2 | Length of frame excluding the "start of frame" and "length" attributes |
| Node ID | 2 | Destination reader - set to 00 00 to broadcast to all |
| Command ID | 2 | Command you're sending to the reader |
| Data | 0 - 208 | Data payload |
| XOR | 1 | XOR each byte from Node ID to Last Data byte with 0xff |
- If an octet with value AA appears anywhere other than the "start of frame" attribute a 00 must be inserted in the next octet but the length will remain unchanged.
- If a response is not returned within 100ms this is considered a failure.
Reply
A reply frame read from the reader consists of:
| Name | Length (octets) | Description |
| Start of frame | 2 | Always set to AA BB |
| Length | 2 | Length of frame excluding the "start of frame" and "length" attributes |
| Node ID | 2 | Destination reader - set to 00 00 to broadcast to all |
| Command ID | 2 | Command you're sending to the reader |
| Status | 1 | 0 indicates success, non-zero indicates error |
| Data | 0 - 208 | Data payload |
| XOR | 1 | XOR each byte from Node ID to Last Data byte with 0xff |
- If an octet with value AA appears anywhere other than the "start of frame" attribute a 00 will be inserted in the next octet but the length will remain unchanged.
Library Functions
Low level
The library should provide low level functions similar to:
send_command(command, data, nodeid=0)::
The length and XOR can be calculated within the function and the frame sent to the reader. The nodeid should default to 0x0000 (broadcast) if not set.
receive_response(timeout=100)::
Receive a reply frame and validate its checksum. Notice must be taken to the length field since you could get a short read (in which case you need to read() again until you get the whole frame) or a long read (you received multiple frames in a single read() call). The timeout should default to 100ms. Returns an error or a dictionary with the fields: nodeid, command, status, data.
Mid level
The send_command() and receive_response() functions can be combined so that a single function call will send a command, block and return a response/error in one go.
High level
Functions can be created for each command which accept the relevant parameters, structure the payload appropriately and parse the response appropriately. If you wanted to go a bit crazy you could object-orientate this a bit (i.e. an object per reader node on the bus, an object per card, etc).
request_anticollision(nodeid=0)::
Send an anticollision request to discover devices, then look for replies (possibly several) for 100ms. If you get errors then retransmit the request. Remember to remove duplicate responses. Once the timeout has expired you can return a list of discovered card IDs. If you're being object orientated you can instantiate a "card" object for each discovered card.
Constants
Constants can be provided for the various command IDs - consult the PDF.
Error conditions
The following are error conditions that must be handled:
- Checksum mismatch
- Timeout before receiving frame
- Timeout in the middle of receiving frame
- Frame doesn't begin with AA BB (means you started reading mid-frame or got a load of junk on the line) - probably best to discard and see if another frame appears within the timeout)
- An AA within the frame isn't followed by 00 (again, probably discard and wait for another frame)
