/sm130/test.py
Python | 381 lines | 348 code | 10 blank | 23 comment | 8 complexity | d3b8e3d9010f6c56a4222fc58ad6242f MD5 | raw file
- import serial
- import time
- import array
- import urllib2
- import sys
- #
- #******************************************************************************************************************************
- #* RFID reader, SM130
- #* From https://www.sparkfun.com/datasheets/Sensors/ID/SM130.pdf
- #* package sent by the host
- #* HEADER:RESERVED:LENGTH (1 byte + data length):COMMAND:DATA (N bytes):CSUM (add all bytes except header)
- #* 0xFF:0x00:0xx:0xx:0xxx
- #*
- #* package sent by the sm130
- #* HEADER:RESERVED:LENGTH (1 on failure, 16 on success):COMMAND (same as sent):RESPONSE:CSUM (add all bytes except header)
- #* 0xFF:0x00:0xx:0xx:0xxx
- #*
- #* Default baud rate 19200bps
- #******************************************************************************************************************************
- #
- # Reference:
- # SM130 https://www.sparkfun.com/datasheets/Sensors/ID/SM130.pdf
- # Smart Poster Record Type Definition: http://www.cardsys.dk/download/NFC_Docs/NFC%20Smart%20Poster%20Record%20Type%20Definition%20Technical%20Specification.pdf
- # TLV tag types: http://apps4android.org/nfc-specifications/NFCForum-TS-Type-2-Tag_1.1.pdf
- # MIFARE MAD: http://www.nxp.com/documents/application_note/AN10787.pdf
- # NDEF: https://learn.adafruit.com/adafruit-pn532-rfid-nfc/ndef
- #
- HEADER = 0xFF
- RESERVED = 0x00
- CMD_RESET = 0x80
- CMD_FIRMWARE_VER = 0x81
- CMD_SEEK_FOR_TAG = 0x82
- CMD_AUTHENTICATE = 0x85
- CMD_READ_BLOCK = 0x86
- CMD_KEY_A = 0xAA
- CMD_KEY_B = 0xBB
- CMD_KEY_TRANSPORT = 0xFF
- ERR_SEEK_FOR_TAG_SUCCESS = 0x4C
- ERR_SEEK_FOR_TAG_RF_OFF = 0x55
- STANDARD_KEY_A = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
- MAD_KEY_A = [0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5]
- NDEF_KEY_A = [0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7]
- TYPE_MIFARE_UL = 0x01
- TYPE_MIFARE_1K = 0x02
- TYPE_MIFARE_4K = 0x03
- MIFARE_UL_BLOCKS = 16
- MIFARE_UL_C_BLOCKS = 48
- MIFARE_1K_BLOCKS = 64
- TLV_NULL = 0x00
- TLV_LOCK = 0x01
- TLV_NDEF = 0x03
- TLV_PROP = 0xFD
- TLV_TERM = 0xFE
- SERIAL_READ_TIMEOUT = 0.02
- RESET_TIMER_S = 3
- def rfidWriteCustomCommand(ser, command):
- barr = array.array('B', command)
- chk = 0
- for i in range(2, len(barr)):
- chk = chk + barr[i]
- barr.append(chk & 0xFF)
- ser.write(barr);
- print "Writing %s" % barr
- # Write simple zero payload command
- def rfidWriteSimpleCommand(ser, command):
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x01, command])
- def getRecordTypePrefix(record_type_indicator, record_field):
- if record_field == 0x00:
- return ""
- if record_field == 0x01:
- return "http://www."
- if record_field == 0x02:
- return "https://www."
- if record_field == 0x03:
- return "http://"
- if record_field == 0x04:
- return "https://"
- if record_field == 0x05:
- return "tel:"
- if record_field == 0x06:
- return "mailto:"
- if record_field == 0x07:
- return "ftp://anonymous:anonymous@"
- if record_field == 0x08:
- return "ftp://ftp."
- if record_field == 0x09:
- return "ftps://"
- if record_field == 0x0A:
- return "sftp://"
- if record_field == 0x0B:
- return "smb://"
- if record_field == 0x0C:
- return "nfs://"
- if record_field == 0x0D:
- return "ftp://"
- if record_field == 0x0E:
- return "dav://"
- if record_field == 0x0F:
- return "news:"
- if record_field == 0x10:
- return "telnet://"
- if record_field == 0x11:
- return "imap:"
- if record_field == 0x12:
- return "rtsp://"
- if record_field == 0x13:
- return "urn:"
- if record_field == 0x14:
- return "pop:"
- if record_field == 0x15:
- return "sip:"
- if record_field == 0x16:
- return "sips:"
- if record_field == 0x17:
- return "tftp:"
- if record_field == 0x18:
- return "btspp://"
- if record_field == 0x19:
- return "btl2cap://"
- if record_field == 0x1A:
- return "btgoep://"
- if record_field == 0x1B:
- return "tcpobex://"
- if record_field == 0x1C:
- return "irdaobex://"
- if record_field == 0x1D:
- return "file://"
- if record_field == 0x1E:
- return "urn:epc:id:"
- if record_field == 0x1F:
- return "urn:epc:tag:"
- if record_field == 0x20:
- return "urn:epc:pat:"
- if record_field == 0x21:
- return "urn:epc:raw:"
- if record_field == 0x22:
- return "urn:epc:"
- if record_field == 0x23:
- return "urn:nfc:"
- def processTarget(target):
- print "Handling target %s" % target
- uri = urllib2.urlopen(target).read()
- urllib2.urlopen("http://127.0.0.1:666/1/%s/play" % uri).read()
- print "uri: %s" % uri
- def rfidProcessNdef(ndef):
- print "Processing ndef: %s" % "".join("%02x" % b for b in ndef)
- # Record header
- ndef_header = ndef.pop(0)
- h_tnf = ndef_header & 0x07
- # We only support 0x01, Well known Record
- if h_tnf != 0x01:
- print "NDEF does not contain a well known record"
- return
- h_il = (ndef_header >> 3) & 1
- h_sr = (ndef_header >> 4) & 1
- h_cf = (ndef_header >> 5) & 1
- h_me = (ndef_header >> 6) & 1
- h_mb = (ndef_header >> 7) & 1
- print "NDEF: message begin: %d, message end: %d, chunk: %d, short: %d, length present: %d, tnf: %d" % (h_mb, h_me, h_cf, h_sr, h_il, h_tnf)
- # Only handle short messages and non chunked.
- if h_cf == 1 or h_sr == 0:
- print "Long or chunked messages currently unsupported"
- return
- type_length = ndef.pop(0)
- payload_length = ndef.pop(0)
- if h_il == 1:
- id_length = ndef.pop(0)
- else:
- id_length = 0
- record_type_indicator = "".join( chr( val ) for val in ndef[0:type_length] )
- del ndef[0:type_length]
- #Skip id, if any
- if id_length > 0:
- del ndef[0:id_length]
- print "type_length: %d, id_length: %d, record_type_indicator: %s" % (type_length, id_length, record_type_indicator)
- # We only support record types U and Sp (Smart poster)
- if record_type_indicator == "U":
- # Uri
- record_field = ndef.pop(0)
- # Rest of data is payload
- prefix = getRecordTypePrefix(record_type_indicator, record_field)
- print "Record prefix: %s " % prefix
- payload_part = ndef[0:payload_length-1] # -1 for the record field
- del ndef[0:payload_length-1]
- payload_str = "".join( chr( val ) for val in payload_part )
- print "Payload %s" % payload_str
- together = "%s%s" % (prefix, payload_str)
- print "Together: %s" % together
- # Just for now, if fjun, open.
- if "fjun.com" in together:
- processTarget(together)
- if len(ndef) > 0:
- # There is more
- rfidProcessNdef(ndef)
- elif record_type_indicator == "T":
- # Text
- status_length = ndef.pop(0)
- language_code = ndef[0:status_length]
- del ndef[0:status_length]
- payload_part = ndef[0:payload_length-1-status_length]
- del ndef[0:payload_length-1-status_length]
- payload_str = "".join( chr( val ) for val in payload_part )
- print "Payload %s" % payload_str
- if len(ndef) > 0:
- # There is more
- rfidProcessNdef(ndef)
- elif record_type_indicator == "Sp":
- # This is a poster, restart Ndef message
- rfidProcessNdef(ndef)
- else:
- print "Unsupported record type indicator %s" % record_type_indicator
- return
- def rfidProcessData(payload):
- print "Processing data: %s" % "".join("%02x" % ord(b) for b in payload)
- # Convert to ord
- for i in range(0, len(payload)):
- payload[i] = ord(payload[i])
- while True:
- # search for TLV record.
- tlv_type = payload.pop(0)
- if tlv_type == TLV_NULL:
- print "Found null TLV, going to next."
- continue
- elif tlv_type == TLV_LOCK:
- tlv_length = payload.pop(0)
- print "Found lock TLV with length %d, will ignore it." % tlv_length
- del payload[0:tlv_length]
- elif tlv_type == TLV_NDEF:
- tlv_length = payload.pop(0)
- print "Found ndef TLV with length %d" % tlv_length
- ndef = payload[0:tlv_length]
- del payload[0:tlv_length]
- rfidProcessNdef(ndef)
- elif tlv_type == TLV_PROP:
- print "Found lock TLV"
- tlv_length = payload.pop(0)
- elif tlv_type == TLV_TERM:
- print "Found term TLV, stopping"
- break
- def rfidRead(ser, end):
- payload = []
- buffer = []
- type = 0x00
- block = 0x00
- reset_counter = 0
- while True:
- byte = ser.read(1)
- if byte == "":
- if end:
- break
- reset_counter = reset_counter + 1
- if reset_counter > (RESET_TIMER_S / SERIAL_READ_TIMEOUT):
- reset_counter = 0
- rfidWriteSimpleCommand(ser, CMD_SEEK_FOR_TAG)
- else:
- reset_counter = 0
- buffer.append(ord(byte))
- print "byte: %x" % ord(byte)
- if len(buffer) >= 4:
- length = buffer[2]
- command = buffer[3]
- print "Command: %x with length: %d" % (command, length)
- # Read bytes + checksum
- data = list(ser.read(length))
- dummy = ser.read(1)
- buffer = []
-
- # Handle commands.
- if command == CMD_SEEK_FOR_TAG and length >= 6:
- # Got card.
- type = ord(data.pop(0))
- print "Got card with type %d and id: %s" % (type, "".join("%02x" % ord(b) for b in data))
- block = 0x04
- payload = []
- # Try to authenticate if Mifare 1k or 4k
- if type == TYPE_MIFARE_1K or type == TYPE_MIFARE_4K:
- if block >= 0x00 and block <= 0x03:
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x09, CMD_AUTHENTICATE, block, CMD_KEY_A, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5]) #MAD
- else:
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x09, CMD_AUTHENTICATE, block, CMD_KEY_A, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7]) #NDEF
- elif type == TYPE_MIFARE_UL:
- # ultralight, no authenticate needed, just read.
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x02, CMD_READ_BLOCK, block])
- if command == CMD_AUTHENTICATE and length >= 2:
- print "CMD_AUTHENTICATE: %s" % "".join("%02x" % ord(b) for b in data)
- # Try to read
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x02, CMD_READ_BLOCK, block])
- if command == CMD_READ_BLOCK and length >= 2:
- # Remove first block number and last chk
- lblock = ord(data.pop(0))
- chk = ord(data.pop(len(data)-1))
- # For now, all zero? stop.
- oneNotZero = False
- for d in data:
- if ord(d) != 0:
- oneNotZero = True
- if oneNotZero:
- payload.extend(data)
- print "CMD_READ_BLOCK (block: %d): %s" % (lblock, "".join("%02x" % ord(b) for b in data))
- if type == TYPE_MIFARE_UL:
- block = block + 4
- if block < MIFARE_UL_C_BLOCKS:
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x02, CMD_READ_BLOCK, block])
- else:
- rfidProcessData(payload)
- else:
- block = block + 1
- # Skip last block in each section.
- if (block+1) % 4 == 0:
- block = block + 1
- if block < MIFARE_1K_BLOCKS:
- rfidWriteCustomCommand(ser, [HEADER, RESERVED, 0x09, CMD_AUTHENTICATE, block, CMD_KEY_A, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7]) #NDEF
- else:
- rfidProcessData(payload)
- else:
- rfidProcessData(payload)
- # Ultralight, record type URI record is 0x55 ('U')
- #data = [0x01, 0x03, 0xa0, 0x0c, 0x34, 0x03, 0x35, 0xd1, 0x01, 0x31, 0x55, 0x02, 0x74, 0x61, 0x67, 0x6f, 0x6e, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x36, 0x34, 0x44, 0x45, 0x33, 0x33, 0x35, 0x43, 0x2d, 0x42, 0x38, 0x31, 0x41, 0x2d, 0x34, 0x36, 0x44, 0x36, 0x2d, 0x42, 0x38, 0x39, 0x46, 0x2d, 0x43, 0x43, 0x39, 0x42, 0x39, 0x35, 0x34, 0x31, 0x39, 0x46, 0x31, 0x33, 0xfe, 0x00]
- #for i in range(0, len(data)):
- # data[i] = chr(data[i])
- #rfidProcessData(data)
- # Mifare 1k, record type 0x53, 0x70
- #data = [0x00, 0x00, 0x03, 0x31, 0xd1, 0x02, 0x2c, 0x53, 0x70, 0x91, 0x01, 0x1d, 0x55, 0x03, 0x66, 0x6a, 0x75, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x66, 0x63, 0x2f, 0x67, 0x65, 0x74, 0x2e, 0x70, 0x68, 0x70, 0x3f, 0x69, 0x64, 0x3d, 0x31, 0x32, 0x33, 0x34, 0x51, 0x01, 0x07, 0x54, 0x02, 0x73, 0x76, 0x74, 0x65, 0x64, 0x74, 0xfe, 0x00]
- #for i in range(0, len(data)):
- # data[i] = chr(data[i])
- #rfidProcessData(data)
- #exit(1)
- port = "COM5"
- if len(sys.argv) > 1:
- port = sys.argv[1]
- ser = serial.Serial(
- port=port,
- baudrate=19200,
- parity=serial.PARITY_NONE,
- stopbits=serial.STOPBITS_ONE,
- bytesize=serial.EIGHTBITS,
- xonxoff=False,
- rtscts=False,
- dsrdtr=False,
- timeout=SERIAL_READ_TIMEOUT
- )
- print ser # check which port was really used
- print ser.isOpen()
- rfidWriteSimpleCommand(ser, CMD_RESET)
- time.sleep(1)
- rfidRead(ser, True)
- rfidWriteSimpleCommand(ser, CMD_SEEK_FOR_TAG)
- rfidRead(ser, True)
- rfidRead(ser, False)
- ser.close() # close port