├── pypn5180 ├── __init__.py ├── pypn5180_15693.py ├── pypn5180.py ├── iso_iec_15693.py └── pypn5180hal.py ├── img ├── pn5180.png └── ftdi2232.png ├── setup.py ├── README.md └── LICENSE /pypn5180/__init__.py: -------------------------------------------------------------------------------- 1 | all = ["pypn5180_15693"] -------------------------------------------------------------------------------- /img/pn5180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/captainbeeheart/pypn5180/HEAD/img/pn5180.png -------------------------------------------------------------------------------- /img/ftdi2232.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/captainbeeheart/pypn5180/HEAD/img/ftdi2232.png -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | setup( 3 | name="pypn5180", 4 | version="1.0", 5 | description="NXP PN5180 python interface using FTDI2232, or directly connected to a raspberry. This python3 module gives an abstraction layer to control NXP PN8180 and implements the NFC ISO-IEC-15693 specification", 6 | url="https://github.com/captainbeeheart", 7 | author = "Captainbeeheart", 8 | author_email = "captainbeehart@protonmail.com", 9 | license="GPL v3.0", 10 | platform="Linux", 11 | packages=find_packages(), 12 | ) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pypn5180 2 | Python interface for NFC-15693 using NXP pn5180 3 | 4 | Running on linux PC with USB/FTDI interface with python3, or on raspberry-pi with python2. 5 | 6 | ## Linux PC setup 7 | 8 | ```bash 9 | sudo apt install python3-pip libusb-1.0 10 | pip3 install wheel 11 | pip3 install setuptools 12 | pip3 install pyftdi 13 | pip3 install progressbar 14 | 15 | # create udev configuration file with following content: 16 | 17 | # /etc/udev/rules.d/11-ftdi.rules 18 | # FT232AM/FT232BM/FT232R 19 | SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", GROUP="plugdev", MODE="0664" 20 | # FT2232C/FT2232D/FT2232H 21 | SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6010", GROUP="plugdev", MODE="0664" 22 | # FT4232/FT4232H 23 | SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6011", GROUP="plugdev", MODE="0664" 24 | # FT232H 25 | SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6014", GROUP="plugdev", MODE="0664" 26 | # FT230X/FT231X/FT234X 27 | SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6015", GROUP="plugdev", MODE="0664" 28 | 29 | sudo udevadm control --reload-rules 30 | sudo udevadm trigger 31 | sudo adduser $USER plugdev 32 | 33 | # Log out and in then unplug and plug FTDI usb. 34 | 35 | sudo python3 setup.py install 36 | ``` 37 | 38 | **Note:** refer to [https://eblot.github.io/pyftdi/installation.html] for complete pyftdi install documentation. 39 | 40 | ## Usage 41 | 42 | ``` bash 43 | # Read FRAM block 5 44 | python3 -m pypn5180.pypn5180_15693 READBLK -o 5 45 | 46 | # Write FRAM block 16 with '0xA1 0xA2 0xA3 0xB4 0xB5 0xB6 0xC7 0xC8' 47 | python3 -m pypn5180.pypn5180_15693 WRITEBLK -o 16 -d A1A2A3B4B5B6C7C8 48 | 49 | # Send a custom or proprietary command 0xA0, with manusacturer id 0x07 and data '0xA1 0xA2 0xA3 0xB4 0xB5 0xB6 0xC7 0xC8' 50 | python3 -m pypn5180.pypn5180_15693 CUSTOM -c A0 -m 07 -d A1A2A3B4B5B6C7C8 51 | 52 | # Maintain power on for a sensor by RF without sending data 53 | python3 -m pypn5180.pypn5180_15693 POWER 54 | 55 | # Dump a complete FRAM content, output file 'UUID-Date.dat' is created 56 | python3 -m pypn5180.pypn5180_15693 DUMP 57 | 58 | # FreestyleLibre Dump data FRAM part (output file: FREE-UUID-Date.dat) 59 | python3 -m pypn5180.pypn5180_15693 FREEDUMP 60 | 61 | ``` 62 | 63 | 64 | ## Connection between ftdi2232 and pn5180 boards 65 | 66 | 67 | 68 | Configuration switch between portA and portB to be done with **-f PORT_x** command: 69 | - **PORT_A: ftdi://ftdi:2232h/1** 70 | - **PORT_B: ftdi://ftdi:2232h/2** 71 | 72 | | NXP5180 | FTDI 2232 | 73 | |---------|--------------| 74 | |+5V | VCC | 75 | |+3V3 | 3V3 | 76 | |RST | 3V3 | 77 | |NSS | BD3 / AD3 | 78 | |MOSI | BD1 / AD1 | 79 | |MISO | BD2 / AD2 | 80 | |SCK | BD0 / AD0 | 81 | |BUSY | - | 82 | |GND | GND | 83 | |GPIO | - | 84 | |IRQ | - | 85 | |AUX | - | 86 | |REQ | - | 87 | 88 | ## Raspberry-pi setup 89 | 90 | need spidev-3.2 at least installed on the raspberry 91 | 92 | | NXP5180 | Raspi Header | 93 | |---------|----------------| 94 | |+5V | 2 - 5V | 95 | |+3V3 | 1 - 3V3 | 96 | |RST | 17- 3V3 | 97 | |NSS | 24- SPI0-CS0 | 98 | |MOSI | 19- SPI0-MOSI| 99 | |MISO | 21- SPI0-MISO| 100 | |SCK | 23- SPI0-SCLK| 101 | |BUSY | - | 102 | |GND | 6 - GND | 103 | |GPIO | - | 104 | |IRQ | - | 105 | |AUX | - | 106 | |REQ | - | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /pypn5180/pypn5180_15693.py: -------------------------------------------------------------------------------- 1 | from pypn5180.iso_iec_15693 import iso_iec_15693 2 | import time 3 | import os 4 | import errno 5 | import binascii 6 | import argparse 7 | import datetime 8 | import struct 9 | import progressbar 10 | 11 | 12 | class pbar(): 13 | def __init__(self): 14 | self.pb = progressbar.ProgressBar().start() 15 | 16 | def updatepb(self, current_block, max_block): 17 | k = int(100*current_block/max_block) 18 | self.pb.update(k) 19 | 20 | def finish(self): 21 | self.pb.finish() 22 | 23 | 24 | def dumpFRAM(binFile): 25 | with open(binFile, 'wb') as fid: 26 | pb = pbar() 27 | print("destination file: %s" %binFile) 28 | for k in range(255): 29 | pb.updatepb(k, 255) 30 | data, errStr = isoIec15693.readSingleBlockCmd(k) 31 | if 'OK' in errStr: 32 | hexdata = bytes(data) 33 | fid.write(hexdata) 34 | pb.finish() 35 | 36 | 37 | def getBlockSecurityStatus(): 38 | for k in range(255): 39 | status = isoIec15693.getMultipleBlockSecurityStatusCmd(k, 1) 40 | print("Status: %r" %status) 41 | 42 | 43 | def displayHelp(): 44 | print("\npypn5180_15693 compatible for RASPBERRY (python2) or on Linux x86 (python3)") 45 | print("Use NXP 5180 board to implement ISO IEC-15693 RF norm") 46 | print("\nSupported commands :") 47 | print("Maintain RF Power On : 'pypn5180.py POWER'") 48 | print("Dump a complete FRAM (output file: UUID-Date.dat) : 'pypn5180.py DUMP'") 49 | print("Read NFC block(x) : 'pypn5180.py READBLK -o x'") 50 | print("Write NFC block(x) 8 bits data=A1A2A3B4B5B6C7C8 : 'pypn5180.py WRITEBLK -o x -d A1A2A3B4B5B6C7C8'") 51 | print("Read Security status block(x) : 'pypn5180.py BLOCKSECURITY -o x'") 52 | print("Send NFC Proprietary command x, 8 bits data=A1A2A3B4B5B6C7C8 : 'pypn5180.py CUSTOM -c x -d A1A2A3B4B5B6C7C8'\n") 53 | 54 | 55 | def parseInputs(): 56 | parser = argparse.ArgumentParser() 57 | parser.add_argument("mode", type=str, default="POWER", help="give test mode: {'DUMP', 'FREEDUMP','CUSTOM', 'POWER', 'READBLK', 'WRITEBLK', 'BLOCKSECURITY'}") 58 | parser.add_argument("-o", "--blockOffset", type=int, default=0, help="Block offset required for READBLK, WRITEBLK") 59 | parser.add_argument("-d","--data", type=str, default="", help="Hexlified datablock to write (8 bytes, requred for WRITEBLK, CUSTOM)") 60 | parser.add_argument("-c", "--custom", type=str, default="A0", help="One hex byte for CUSTOM command code ex: A0") 61 | parser.add_argument("-m", "--mfCode", type=str, default="07", help="Manufacturer Code ID") 62 | parser.add_argument("-f", "--ftdi_port", type=str, default="PORT_A", help="FTDI 2232 port 'PORT_A, PORT_B'") 63 | return parser.parse_args() 64 | 65 | 66 | if __name__ == "__main__": 67 | 68 | args = parseInputs() 69 | 70 | isoIec15693 = iso_iec_15693(args.ftdi_port) 71 | sys_info, errStr = isoIec15693.getSystemInformationCmd() 72 | serial = binascii.hexlify(bytes(sys_info[1:9])).decode('utf-8') 73 | print('[%s] SysInfo - chip serial: %r' %(errStr, serial)) 74 | 75 | if args.mode == "POWER": 76 | # Maintain RF power on until 'CTRL+C' pressed 77 | while True: 78 | time.sleep(1) 79 | 80 | elif args.mode == "DUMP": 81 | date = ("%s" %datetime.datetime.now()).replace(" ", "-") 82 | dumpFRAM(serial + date + ".dat") 83 | 84 | elif args.mode == "FREEDUMP": 85 | date = ("FREE-%s" %datetime.datetime.now()).replace(" ", "-") 86 | dumpFREE(serial + date + ".dat") 87 | 88 | elif args.mode == "CUSTOM": 89 | if args.data is not "": 90 | dataIn = list(binascii.unhexlify(args.data)) 91 | else: 92 | dataIn = [] 93 | cmdCode = ord(binascii.unhexlify(args.custom)) 94 | mfCode = ord(binascii.unhexlify(args.mfCode)) 95 | print("Sending Code 0x%x with %r" %(cmdCode,dataIn)) 96 | data, errStr = isoIec15693.customCommand(cmdCode, mfCode, dataIn) 97 | if "No Answer from tag" not in errStr: 98 | print("CMD %x: [%s] Data: [%r] - [%s]" %(cmdCode, errStr, data, binascii.hexlify(data))) 99 | else: 100 | print("%s" %errStr) 101 | 102 | elif args.mode == "READBLK": 103 | data, errStr = isoIec15693.readSingleBlockCmd(args.blockOffset) 104 | if 'OK' in errStr: 105 | print("Block: %r / %s" %(data, binascii.hexlify(data))) 106 | 107 | elif args.mode == "WRITEBLK": 108 | dataToSend = list(binascii.unhexlify(args.data)) 109 | data, errStr = isoIec15693.writeSingleBlockCmd(args.blockOffset, dataToSend) 110 | print("Write Block: %s" %errStr) 111 | 112 | elif args.mode == "BLOCKSECURITY": 113 | dataToSend = list(binascii.unhexlify(args.data)) 114 | data, errStr = isoIec15693.getMultipleBlockSecurityStatusCmd(args.blockOffset, 1) 115 | print("Write Block: %s" %errStr) 116 | 117 | 118 | else: 119 | print("Unknown command") 120 | 121 | isoIec15693.disconnect() 122 | -------------------------------------------------------------------------------- /pypn5180/pypn5180.py: -------------------------------------------------------------------------------- 1 | import time 2 | import struct 3 | import binascii 4 | from . import pypn5180hal 5 | 6 | 7 | """ 8 | PN5180 main class providing NFC functions to initialise the chip and send/receive NFC frames. 9 | """ 10 | class PN5180(pypn5180hal.PN5180_HIL): 11 | 12 | RF_ON_MODE={ 13 | 'STANDARD':0x00, 14 | 'IEC_18092_COLLISION_DISABLE':0x1, # disable collision avoidance according to ISO/IEC 18092 15 | 'IEC_18092_ACVTIVE_COMMUNICATION':0x2 # Use Active Communication mode according to ISO/IEC 18092 16 | } 17 | 18 | MAX_REGISTER_ADDR = 0x29 19 | 20 | """ 21 | getFirmwareVersion(self) 22 | response : 2 bytes 23 | """ 24 | def getFirmwareVersion(self): 25 | firmwareVersion = self.readEeprom(self.EEPROM_ADDR['FIRMWARE_VERSION'], 2) 26 | return self._toInt16(firmwareVersion) 27 | 28 | 29 | """ 30 | getProductVersion(self) 31 | response : 2 bytes 32 | """ 33 | def getProductVersion(self): 34 | productVersion = self.readEeprom(self.EEPROM_ADDR['PRODUCT_VERSION'], 2) 35 | return self._toInt16(productVersion) 36 | 37 | 38 | """ 39 | getEepromVersion(self) 40 | response : 2 bytes 41 | """ 42 | def getEepromVersion(self): 43 | eepromVersion = self.readEeprom(self.EEPROM_ADDR['EEPROM_VERSION'], 2) 44 | return self._toInt16(eepromVersion) 45 | 46 | """ 47 | getDieIdentifier(self) 48 | response : 2 bytes 49 | """ 50 | def getDieIdentifier(self): 51 | dieIdentifier = self.readEeprom(self.EEPROM_ADDR['DIE_IDENTIFIER'], 16) 52 | return self._toHex(dieIdentifier) 53 | 54 | """ 55 | selfTest(self) 56 | Display PN5180 chip versions (HW, SW) 57 | """ 58 | def selfTest(self): 59 | # Get firmware version from EEPROM 60 | firmwareVersion = self.getFirmwareVersion() 61 | productVersion = self.getProductVersion() 62 | eepromVersion = self.getEepromVersion() 63 | dieIdentifier = self.getDieIdentifier() 64 | print(" Firmware version: %#x" % firmwareVersion) 65 | print(" Product Version : %#x" % productVersion) 66 | print(" EEPROM version : %#x" % eepromVersion) 67 | print(" Die identifier : %#r" % dieIdentifier) 68 | 69 | 70 | """ 71 | dumpRegisters(self) 72 | Dumps and display all PN5180 registers 73 | """ 74 | def dumpRegisters(self): 75 | print("======= Register Dump =======") 76 | for addr in range(0, self.MAX_REGISTER_ADDR): 77 | registerValue = self.readRegister(addr) 78 | print("%s %#x = %#x (%r)" %(self.REGISTER_NAME[addr], addr, registerValue, bin(registerValue))) 79 | registerValue = self.readRegister(0x39) 80 | print("%s %#x = %#x (%r)" %(self.REGISTER_NAME[0x39], 0x39, registerValue, bin(registerValue))) 81 | print("=============================") 82 | 83 | 84 | """ 85 | configureIsoIec15693Mode(self) 86 | Soft reset, configure default parameters for Iso IEC 15693 and enable RF 87 | """ 88 | def configureIsoIec15693Mode(self): 89 | # TODO : 90 | # - do a clean interface selector, not hard coded 91 | # - Configure CRC registers 92 | self.softwareReset() 93 | 94 | # RF_CFG = { 95 | # 'TX_ISO_15693_ASK100':0x0D, # 26 kbps 96 | # 'RX_ISO_15693_26KBPS':0x8D, # 26 kbps 97 | # 'TX_ISO_15693_ASK10':0x0E, # 26 kbps 98 | # 'RX_ISO_15693_53KBPS':0x8E # 53 kbps 99 | # } 100 | self.loadRfConfig(self.RF_CFG['TX_ISO_15693_ASK100'], self.RF_CFG['RX_ISO_15693_26KBPS']) 101 | self.rfOn(self.RF_ON_MODE["STANDARD"]) 102 | 103 | # Set SYSTEM regsiter state machine to transceive 104 | self.setSystemCommand("COMMAND_IDLE_SET") 105 | 106 | """ 107 | transactionIsoIec15693(cmd) 108 | Perform RF transaction. Send command to the RFiD device and read device result. 109 | """ 110 | def transactionIsoIec15693(self, command): 111 | self.setSystemCommand("COMMAND_TRANSCEIVE_SET") 112 | 113 | # Check RF_STATUS TRANSCEIVE_STATE value 114 | # must be WAIT_TRANSMIT 115 | if self.getRfStatusTransceiveState() is not "WAIT_TRANSMIT": 116 | print("transactionIsoIec15693 Error in RF state: %s" %self.getRfStatusTransceiveState()) 117 | return -1 118 | 119 | self.sendData(8,command) 120 | self._usDelay(50000) # 50 ms 121 | nbBytes = self.getRxStatusNbBytesReceived() 122 | response = self.readData(nbBytes) 123 | if response: 124 | flags = response[0] 125 | data = response[1:] 126 | # print("Received %d bytes from sensor: [flags]: %x, [data]: %r" %(nbBytes, flags, [hex(x) for x in data])) 127 | else: 128 | flags = 0xFF 129 | data = [] 130 | 131 | self.setSystemCommand("COMMAND_IDLE_SET") 132 | 133 | return flags, data 134 | 135 | 136 | def getRfStatusTransceiveState(self): 137 | regvalue = self.readRegister(self.REG_ADDR['RF_STATUS']) 138 | transceiveState = (regvalue >> 24) & 0x3 139 | return self.RF_STATUS_TRANSCEIVE_STATE[transceiveState] 140 | 141 | 142 | def getRxStatusNbBytesReceived(self): 143 | regvalue = self.readRegister(self.REG_ADDR['RX_STATUS']) 144 | return regvalue & 0x1FF 145 | 146 | 147 | def setSystemCommand(self, mode): 148 | self.writeRegisterAndMask(self.REG_ADDR["SYSTEM_CONFIG"],self.SYSTEM_CONFIG["COMMAND_CLR"]) 149 | self.writeRegisterOrMask(self.REG_ADDR["SYSTEM_CONFIG"],self.SYSTEM_CONFIG[mode]) 150 | 151 | 152 | def softwareReset(self): 153 | self.writeRegisterOrMask(self.REG_ADDR["SYSTEM_CONFIG"],self.SYSTEM_CONFIG["RESET_SET"]) 154 | self._usDelay(50000) # 50ms 155 | self.writeRegisterAndMask(self.REG_ADDR["SYSTEM_CONFIG"],self.SYSTEM_CONFIG["RESET_CLR"]) 156 | self._usDelay(50000) # 50ms 157 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /pypn5180/iso_iec_15693.py: -------------------------------------------------------------------------------- 1 | from . import pypn5180 2 | import binascii 3 | import collections 4 | """ 5 | Implementation of ISO-IEC-15693 norm for PN5180 chipset 6 | """ 7 | class iso_iec_15693(object): 8 | 9 | CMD_CODE= { 10 | 'INVENTORY':0x01, 11 | 'STAY_QUIET':0x02, 12 | 'READ_SINGLE_BLOCK':0x20, 13 | 'WRITE_SINGLE_BLOCK':0x21, 14 | 'LOCK_BLOCK':0x22, 15 | 'READ_MULTIPLE_BLOCK':0x23, 16 | 'WRITE_MULTIPLE_BLOCK':0x24, 17 | 'SELECT':0x25, 18 | 'RESET_READY':0x26, 19 | 'WRITE_AFI':0x27, 20 | 'LOCK_AFI':0x28, 21 | 'WRITE_DSFID':0x29, 22 | 'LOCK_DSFID':0x2A, 23 | 'GET_SYSTEM_INFORMATION':0x2B, 24 | 'GET_MULTIPLE_BLOCK_SECURITY_STATUS':0x2C, 25 | 'CUSTOM_READ_SINGLE':0xC0, 26 | 'CUSTOM_WRITE_SINGLE':0xC1, 27 | 'CUSTOM_LOCK_BLOCK':0xC2, 28 | 'CUSTOM_READ_MULTIPLE':0xC3, 29 | 'CUSTOM_WRITE_MULTIPLE':0xC4, 30 | } 31 | 32 | ERROR_CODE = { 33 | 0x00:'ERROR CODE ZERO', 34 | 0x01:'The command is not supported, i.e. the request code is not recognised.', 35 | 0x02:'The command is not recognised, for example: a format error occurred.', 36 | 0x03:'The option is not supported.', 37 | 0x0F:'Unknown error.', 38 | 0x10:'The specified block is not available (doesn t exist).', 39 | 0x11:'The specified block is already -locked and thus cannot be locked again', 40 | 0x12:'The specified block is locked and its content cannot be changed.', 41 | 0x13:'The specified block was not successfully programmed.', 42 | 0x14:'The specified block was not successfully locked', 43 | 0xA7:'CUSTOM ERROR 0xA7' 44 | } 45 | # Avoid unhandled error codes crash: 46 | ERROR_CODE = collections.defaultdict(lambda:0,ERROR_CODE) 47 | 48 | def __init__(self, ftdi_port = "PORT_A"): 49 | print("Connecting to PN5180 device...") 50 | self.pn5180 = pypn5180.PN5180(debug="PN5180", ftdi_port = ftdi_port) 51 | print("PN5180 Self test:") 52 | self.pn5180.selfTest() 53 | print("\nConfiguring device for ISO IEC 15693") 54 | self.pn5180.configureIsoIec15693Mode() 55 | 56 | # Set default frame flags byte: 57 | # [Extract From ISO_IEC_15693] 58 | # Bit 1 Sub-carrier_flag 0 A single sub-carrier frequency shall be used by the VICC 59 | # 1 Two sub-carriers shall be used by the VICC 60 | # Bit 2 Data_rate_flag 0 Low data rate shall be used 61 | # 1 High data rate shall be used 62 | # Bit 3 Inventory_flag 0 Flags 5 to 8 meaning is according to table 4 63 | # 1 Flags 5 to 8 meaning is according to table 5 64 | # Bit 4 Protocol 0 No protocol format extension 65 | # Extension_flag 1 Protocol format is extended. Reserved for future use 66 | self.flags = 0x02 67 | 68 | """ 69 | configureFlags(self, flags) 70 | Configure the flags byte to be used for next transmissions 71 | flags: 1 byte, following ISO_IEC_15693 requirements 72 | """ 73 | def configureFlags(self, flags): 74 | self.flags = flags 75 | 76 | """ 77 | getError(self, flags, data) 78 | analyse error code returned by the RFID chip 79 | """ 80 | def getError(self, flags, data): 81 | 82 | if flags == 0xFF: 83 | return "Transaction ERROR: No Answer from tag" 84 | elif flags != 0: 85 | return "Transaction ERROR: %s" %self.ERROR_CODE[data[0]] 86 | return "Transaction OK" 87 | 88 | def inventoryCmd(self): 89 | pass 90 | # 01h 91 | 92 | 93 | def stayQuietCmd(self, uid): 94 | frame = [] 95 | frame.insert(0, self.flags) 96 | frame.insert(1, self.CMD_CODE['STAY_QUIET']) 97 | frame.extend(uid) 98 | 99 | 100 | def readSingleBlockCmd(self, blockNumber, uid=[]): 101 | frame = [] 102 | frame.insert(0, self.flags) 103 | frame.insert(1, self.CMD_CODE['READ_SINGLE_BLOCK']) 104 | if uid is not []: 105 | frame.extend(uid) 106 | # TODO : Add uid bit in flags 107 | frame.append(blockNumber) 108 | flags, data = self.pn5180.transactionIsoIec15693(frame) 109 | error = self.getError(flags, data) 110 | return data, error 111 | 112 | 113 | def disconnect(self): 114 | self.pn5180.rfOff() 115 | 116 | 117 | def writeSingleBlockCmd(self, blockNumber, data, uid=[]): 118 | #'21' 119 | 120 | if len(data) is not 8: 121 | print("WARNING, data block length must be 8 bytes") 122 | 123 | frame = [] 124 | frame.insert(0, self.flags) 125 | frame.insert(1, self.CMD_CODE['WRITE_SINGLE_BLOCK']) 126 | if uid is not []: 127 | frame.extend(uid) 128 | # TODO : Add uid bit in flags 129 | frame.append(blockNumber) 130 | frame.extend(data) 131 | flags, data = self.pn5180.transactionIsoIec15693(frame) 132 | error = self.getError(flags, data) 133 | return data, error 134 | 135 | 136 | def lockBlockCmd(self, numberOfBlocks, uid=[]): 137 | #'22' 138 | frame = [] 139 | frame.insert(0, self.flags) 140 | frame.insert(1, self.CMD_CODE['LOCK_BLOCK']) 141 | if uid is not []: 142 | frame.extend(uid) 143 | frame.extend(numberOfBlocks) 144 | flags, data = self.pn5180.transactionIsoIec15693(frame) 145 | error = self.getError(flags, data) 146 | return data, error 147 | 148 | 149 | def readMultipleBlocksCmd(self, firstBlockNumber, numberOfBlocks, uid=[]): 150 | frame = [] 151 | frame.insert(0, self.flags) 152 | frame.insert(1, self.CMD_CODE['READ_MULTIPLE_BLOCKS']) 153 | if uid is not []: 154 | frame.extend(uid) 155 | frame.extend(firstBlockNumber) 156 | frame.extend(numberOfBlocks) 157 | flags, data = self.pn5180.transactionIsoIec15693(frame) 158 | error = self.getError(flags, data) 159 | return data, error 160 | 161 | def writeMultipleBlocksCmd(self): 162 | pass 163 | #'24' 164 | 165 | def selectCmd(self, uid): 166 | #'25' 167 | frame = [] 168 | frame.insert(0, self.flags) 169 | frame.insert(1, self.CMD_CODE['SELECT']) 170 | frame.extend(uid) 171 | flags, data = self.pn5180.transactionIsoIec15693(frame) 172 | error = self.getError(flags, data) 173 | return data, error 174 | 175 | 176 | def resetToReadyCmd(self, uid=[]): 177 | #'26' 178 | frame = [] 179 | frame.insert(0, self.flags) 180 | frame.insert(1, self.CMD_CODE['RESET_READY']) 181 | if uid is not []: 182 | frame.extend(uid) 183 | flags, data = self.pn5180.transactionIsoIec15693(frame) 184 | error = self.getError(flags, data) 185 | return data, error 186 | 187 | 188 | def writeAfiCmd(self, afi, uid=[]): 189 | #27' 190 | frame = [] 191 | frame.insert(0, self.flags) 192 | frame.insert(1, self.CMD_CODE['WRITE_AFI']) 193 | if uid is not []: 194 | frame.extend(uid) 195 | frame.extend(afi) 196 | flags, data = self.pn5180.transactionIsoIec15693(frame) 197 | error = self.getError(flags, data) 198 | return data, error 199 | 200 | 201 | def lockAfiCmd(self, uid=[]): 202 | #'28' 203 | frame = [] 204 | frame.insert(0, self.flags) 205 | frame.insert(1, self.CMD_CODE['LOCK_AFI']) 206 | if uid is not []: 207 | frame.extend(uid) 208 | flags, data = self.pn5180.transactionIsoIec15693(frame) 209 | error = self.getError(flags, data) 210 | return data, error 211 | 212 | 213 | def writeDsfidCmd(self, dsfid, uid=[]): 214 | #'29' 215 | frame = [] 216 | frame.insert(0, self.flags) 217 | frame.insert(1, self.CMD_CODE['WRITE_DSFID']) 218 | if uid is not []: 219 | frame.extend(uid) 220 | frame.extend(dsfid) 221 | flags, data = self.pn5180.transactionIsoIec15693(frame) 222 | error = self.getError(flags, data) 223 | return data, error 224 | 225 | 226 | def locckDsfidCmd(self, uid=[]): 227 | #'2A' 228 | frame = [] 229 | frame.insert(0, self.flags) 230 | frame.insert(1, self.CMD_CODE['LOCK_DSFID']) 231 | if uid is not []: 232 | frame.extend(uid) 233 | flags, data = self.pn5180.transactionIsoIec15693(frame) 234 | error = self.getError(flags, data) 235 | return data, error 236 | 237 | 238 | def getSystemInformationCmd(self, uid=[]): 239 | #'2B' 240 | frame = [] 241 | frame.insert(0, self.flags) 242 | frame.insert(1, self.CMD_CODE['GET_SYSTEM_INFORMATION']) 243 | if uid is not []: 244 | frame.extend(uid) 245 | flags, data = self.pn5180.transactionIsoIec15693(frame) 246 | error = self.getError(flags, data) 247 | return data, error 248 | 249 | 250 | def getMultipleBlockSecurityStatusCmd(self, firstBlockNumber, numberOfBlocks, uid=[]): 251 | #'2C' 252 | frame = [] 253 | frame.insert(0, self.flags) 254 | frame.insert(1, self.CMD_CODE['GET_MULTIPLE_BLOCK_SECURITY_STATUS']) 255 | if uid is not []: 256 | frame.extend(uid) 257 | frame.append(firstBlockNumber) 258 | frame.append(numberOfBlocks) 259 | flags, data = self.pn5180.transactionIsoIec15693(frame) 260 | error = self.getError(flags, data) 261 | return data, error 262 | 263 | 264 | def customCommand(self, cmdCode, mfCode, data): 265 | # 'A0' - 'DF' Custom IC Mfg dependent 266 | # 'E0' - 'FF' Proprietary IC Mfg dependent 267 | frame = [] 268 | frame.insert(0, self.flags) 269 | frame.insert(1, cmdCode) 270 | frame.insert(2, mfCode) 271 | if data is not []: 272 | frame.extend(data) 273 | flags, data = self.pn5180.transactionIsoIec15693(frame) 274 | error = self.getError(flags, data) 275 | return data, error 276 | 277 | """ 278 | Note: firstBlockNumber: 2 bytes, LSB first 279 | """ 280 | def customReadSinlge(self, mfCode, firstBlockNumber, uid=[]): 281 | frame = [] 282 | frame.insert(0, self.flags) 283 | frame.insert(1, self.CMD_CODE['CUSTOM_READ_SINGLE']) 284 | frame.insert(2, mfCode) 285 | if uid is not []: 286 | frame.extend(uid) 287 | if len(firstBlockNumber) == 1: 288 | frame.extend(0) 289 | frame.extend(firstBlockNumber) 290 | flags, data = self.pn5180.transactionIsoIec15693(frame) 291 | error = self.getError(flags, data) 292 | return data, error 293 | 294 | """ 295 | Note: firstBlockNumber: 2 bytes, LSB first 296 | """ 297 | def customWriteSinlge(self, cmdCode, mfCode, firstBlockNumber, data, uid=[]): 298 | pass 299 | 300 | 301 | def rfuCommand(self, cmdCode, data, uid=[]): 302 | frame = [] 303 | frame.insert(0, self.flags) 304 | frame.insert(1, cmdCode) 305 | frame.extend(map(ord,data)) 306 | flags, data = self.pn5180.transactionIsoIec15693(frame) 307 | error = self.getError(flags, data) 308 | return data, error 309 | -------------------------------------------------------------------------------- /pypn5180/pypn5180hal.py: -------------------------------------------------------------------------------- 1 | import time 2 | import struct 3 | import binascii 4 | from os import sys 5 | 6 | if sys.version_info[0] < 3: 7 | PY_VERSION = 2 8 | else: 9 | PY_VERSION = 3 10 | 11 | try: 12 | from pyftdi import spi 13 | SPI_DEVICE = "FTDI" 14 | 15 | except: 16 | try: 17 | import spidev 18 | SPI_DEVICE = "RASPI" 19 | except : 20 | print("** Error importing SPI interface. Need spidev on RASPI (python 2.7) or pyftdi (python 3) on X86 **") 21 | SPI_DEVICE = "ERROR" 22 | sys.exit() 23 | 24 | 25 | 26 | class _spi(): 27 | 28 | def __init__(self, bus=0, device=0, speed=1e6, ftdi_port="PORT_A"): 29 | if SPI_DEVICE is "RASPI": 30 | self.device = spidev.SpiDev() 31 | self.device.open(bus, device) 32 | self.device.max_speed_hz = speed 33 | self.xfer = self.device.xfer 34 | 35 | elif SPI_DEVICE is "FTDI": 36 | # Configure FTDI PORT A or PORT B here: 37 | # Port A: ftdi://ftdi:2232h/1 38 | # Port B: ftdi://ftdi:2232h/2 39 | if ftdi_port == "PORT_A": 40 | ftdi_devid = "ftdi://ftdi:2232h/1" 41 | else: 42 | ftdi_devid = "ftdi://ftdi:2232h/2" 43 | 44 | self.device = spi.SpiController() 45 | self.device.configure(ftdi_devid) 46 | self.slave = self.device.get_port(cs=0, freq=speed, mode=0) 47 | self.xfer = self.ftdi_xfer 48 | print("Conected to FTDI SPI %s" %ftdi_devid) 49 | else: 50 | self.device = None 51 | self.xfer = None 52 | 53 | def ftdi_xfer(self, xfert_data): 54 | data = bytearray(bytes(xfert_data)) 55 | # print('TxData: %r' %data) 56 | read_buf = self.slave.exchange(data, duplex=True) 57 | # print('RxData: %r' %read_buf) 58 | return read_buf 59 | 60 | 61 | """ 62 | Hardware interface layer: 63 | This class defines basic access commands to the PN5180 as specified 64 | in the NXP-PN5180A0xx/C1/C2 Datasheet 65 | """ 66 | class PN5180_HIL(object): 67 | # Commands Details 68 | # NXP-PN5180A0xx/C1/C2 Datasheet 69 | CMD = { 70 | 'WRITE_REGISTER':0x00, # Write one 32bit register value 71 | 'WRITE_REGISTER_OR_MASK':0x01, # Sets one 32bit register value using a 32 bit OR mask 72 | 'WRITE_REGISTER_AND_MASK':0x02, # Sets one 32bit register value using a 32 bit AND mask 73 | 'WRITE_REGISTER_MULTIPLE':0x03, # Processes an array of register addresses in random order and performs the defined action on these addresses. 74 | 'READ_REGISTER':0x04, # Reads one 32bit register value 75 | 'READ_REGISTER_MULTIPLE':0x05, # Reads from an array of max.18 register addresses in random order 76 | 'WRITE_EEPROM':0x06, # Processes an array of EEPROM addresses in random order and writes the value to these addresses 77 | 'READ_EEPROM':0x07, # Processes an array of EEPROM addresses from a start address and reads the values from these addresses 78 | 'WRITE_TX_DATA':0x08, # This instruction is used to write data into the transmission buffer 79 | 'SEND_DATA':0x09, # This instruction is used to write data into the transmission buffer, the START_SEND bit is automatically set. 80 | 'READ_DATA':0x0A, # This instruction is used to read data from reception buffer, after successful reception. 81 | 'SWITCH_MODE':0x0B, # This instruction is used to switch the mode. It is only possible to switch from NormalMode to standby, LPCD or Autocoll 82 | 'MIFARE_AUTHENTICATE':0x0C, # This instruction is used to perform a MIFARE Classic Authentication on an activated card. 83 | 'EPC_INVENTORY':0x0D, # This instruction is used to perform an inventory of ISO18000-3M3 tags. 84 | 'EPC_RESUME_INVENTORY':0x0E, # This instruction is used to resume the inventory algorithm in case it is paused. 85 | 'EPC_RETRIEVE_INVENTORY_RESULT_SIZE':0x0F, # This instruction is used to retrieve the size of the inventory result. 'EPC_RETRIEVE_INVENTORY_RESULT':0x10, This instruction is used to retrieve the result of a preceding EPC_INVENTORY or EPC_RESUME_INVENTORY instruction. 86 | 'LOAD_RF_CONFIG':0x11, # This instruction is used to load the RF configuration from EEPROM into the configuration registers. 87 | 'UPDATE_RF_CONFIG':0x12, # This instruction is used to update the RF configuration within EEPROM. 88 | 'RETRIEVE_RF_CONFIG_SIZE':0x13, # This instruction is used to retrieve the number of registers for a selected RF configuration 89 | 'RETRIEVE_RF_CONFIG':0x14, # This instruction is used to read out an RF configuration. The register address-value-pairs are available in the response 90 | 'RF_ON':0x16, # This instruction switch on the RF Field 91 | 'RF_OFF':0x17, # This instruction switch off the RF Field 92 | 'CONFIGURE_TESTBUS_DIGITAL':0x18,# Enables the Digital test bus 93 | 'CONFIGURE_TESTBUS_ANALOG':0x19, # Enables the Analog test bus 94 | } 95 | 96 | REG_ADDR = { 97 | 'SYSTEM_CONFIG': 0x00, 98 | 'RX_STATUS': 0x13, 99 | 'CRC_TX_CONFIG': 0x19, 100 | 'RF_STATUS': 0x1D 101 | } 102 | 103 | REGISTER_NAME = { 104 | 0x0: "SYSTEM_CONFIG", 105 | 0x1: "IRQ_ENABLE", 106 | 0x2: "IRQ_STATUS", 107 | 0x3: "IRQ_CLEAR", 108 | 0x4: "TRANSCEIVER_CONFIG", 109 | 0x5: "PADCONFIG", 110 | 0x6: "RFU", 111 | 0x7: "PADOUT", 112 | 0x8: "TIMER0_STATUS", 113 | 0x9: "TIMER1_STATUS", 114 | 0xA: "TIMER2_STATUS", 115 | 0xB: "TIMER0_RELOAD", 116 | 0xC: "TIMER1_RELOAD", 117 | 0xD: "TIMER2_RELOAD", 118 | 0xE: "TIMER0_CONFIG", 119 | 0xF: "TIMER1_CONFIG", 120 | 0x10: "TIMER2_CONFIG", 121 | 0x11: "RX_WAIT_CONFIG", 122 | 0x12: "CRC_RX_CONFIG", 123 | 0x13: "RX_STATUS", 124 | 0x14: "TX_UNDERSHOOT_CONFIG", 125 | 0x15: "TX_OVERSHOOT_CONFIG", 126 | 0x16: "TX_DATA_MOD", 127 | 0x17: "TX_WAIT_CONFIG", 128 | 0x18: "TX_CONFIG", 129 | 0x19: "CRC_TX_CONFIG", 130 | 0x1A: "SIGPRO_CONFIG", 131 | 0x1B: "SIGPRO_CM_CONFIG", 132 | 0x1C: "SIGPRO_RM_CONFIG", 133 | 0x1D: "RF_STATUS", 134 | 0x1E: "AGC_CONFIG", 135 | 0x1F: "AGC_VALUE", 136 | 0x20: "RF_CONTROL_TX", 137 | 0x21: "RF_CONTROL_TX_CLK", 138 | 0x22: "RF_CONTROL_RX", 139 | 0x23: "LD_CONTROL", 140 | 0x24: "SYSTEM_STATUS", 141 | 0x25: "TEMP_CONTROL", 142 | 0x26: "CECK_CARD_RESULT", 143 | 0x27: "DPC_CONFIG", 144 | 0x28: "EMD_CONTROL", 145 | 0x29: "ANT_CONTROL", 146 | 0x39: "SIGPRO_RM_CONFIG_EXTENSION" 147 | } 148 | 149 | SYSTEM_CONFIG = { 150 | 'RESET_SET':0x00000100, 151 | 'RESET_CLR':0xFFFFFEFF, 152 | 'START_SEND_SET':0x00000008, 153 | 'START_SEND_CLR':0xFFFFFFF7, 154 | 'COMMAND_CLR':0xFFFFFFF8, 155 | 'COMMAND_IDLE_SET':0x00000000, 156 | 'COMMAND_TRANSCEIVE_SET':0x00000003, 157 | 'COMMAND_KEEP_COMMAND_SET':0x00000004, 158 | 'COMMAND_LOOPBACK_COMMAND_SET':0x00000005, 159 | 'COMMAND_PRBS_SET':0x00000006 160 | } 161 | 162 | RF_STATUS_TRANSCEIVE_STATE = { 163 | 0 : "IDLE", 164 | 1 : "WAIT_TRANSMIT", 165 | 2 : "TRANSMITTING", 166 | 3 : "WAIT_RECEIVE", 167 | 4 : "WAIT_FOR_DATA", 168 | 5 : "RECEIVING", 169 | 6 : "LOOPBACK", 170 | 7 : "RESERVED" 171 | } 172 | 173 | RF_CFG = { 174 | 'TX_ISO_15693_ASK100':0x0D, # 26 kbps 175 | 'RX_ISO_15693_26KBPS':0x8D, # 26 kbps 176 | 'TX_ISO_15693_ASK10':0x0E, # 26 kbps 177 | 'RX_ISO_15693_53KBPS':0x8E # 53 kbps 178 | } 179 | 180 | EEPROM_ADDR = { 181 | 'DIE_IDENTIFIER':0x00, # Size: 16 bytes 182 | 'PRODUCT_VERSION':0x10, # Size: 2 bytes 183 | 'FIRMWARE_VERSION':0x12, # Size: 2 bytes 184 | 'EEPROM_VERSION':0x14 # Size: 2 bytes 185 | } 186 | 187 | """ 188 | Debug values : PN5180_HIL, PN5180 189 | """ 190 | def __init__(self, bus=0, device=0, speed=50000, ftdi_port="PORT_A", debug="PN5180_HIL"): 191 | try: 192 | self.debug = debug 193 | self.spi = _spi(bus, device, speed, ftdi_port) 194 | 195 | except IOError as exc: 196 | print("Error opening SPI device : %r" %exc) 197 | sys.exit() 198 | 199 | 200 | def _usDelay(self, useconds): 201 | time.sleep(useconds / 1000000.0) 202 | 203 | 204 | def _getResponse(self, responseLen): 205 | # Send 0xFF bytes to get response bytes if any 206 | if responseLen != 0: 207 | return self.spi.xfer([0xff]*responseLen) 208 | else: 209 | return [] 210 | 211 | 212 | def _sendCommand(self, cmd, parameters, responseLen=0): 213 | # Send [cmd][parametes] 214 | # print('Sending parameters %r' %parameters) 215 | parameters.insert(0, cmd) 216 | dir(self.spi) 217 | self.spi.xfer(parameters) 218 | if self.debug is 'PN5180_HIL': 219 | print("SPI send frame: %r" %(parameters)) 220 | self._usDelay(5000) # TODO : Manage busy signal instead of hard sleep 221 | return self._getResponse(responseLen) 222 | 223 | 224 | # FIXME: python2/3 support, better way ? 225 | def _toList(self, num32): 226 | if PY_VERSION == 2: 227 | return map(ord,list(struct.pack(" %r" %(parameters, content)) 273 | return self._sendCommand(self.CMD['WRITE_REGISTER'], parameters, 0) 274 | 275 | 276 | """ 277 | writeRegisterOrMask(self, address, orMask) 278 | address : 1 byte, Register address 279 | orMask : 4 bytes, 32-bit OR mask (little endian). 280 | response : - 281 | """ 282 | def writeRegisterOrMask(self, address, orMask): 283 | parameters = [] 284 | parameters.insert(0, address) 285 | parameters = parameters + self._toList(orMask) 286 | return self._sendCommand(self.CMD['WRITE_REGISTER_OR_MASK'], parameters, 0) 287 | 288 | 289 | """ 290 | writeRegisterAndMask(self, address, andMask) 291 | address : 1 byte, Register address 292 | andMask : 4 bytes, 32-bit AND mask (little endian). 293 | response : - 294 | """ 295 | def writeRegisterAndMask(self, address, andMask): 296 | parameters = [] 297 | parameters.insert(0, address) 298 | parameters = parameters + self._toList(andMask) 299 | return self._sendCommand(self.CMD['WRITE_REGISTER_AND_MASK'], parameters, 0) 300 | 301 | 302 | """ 303 | writeRegisterMultiple(self, address, parameter) 304 | address : 1 byte, Register address 305 | parameter: Array of up to 42 elements [address, action, content] 306 | address: 1 byte 307 | action : 1 byte (0x01 WRITE_REGISTER, 0x02 WRITE_REGISTER_OR_MASK, 0x03 WRITE_REGISTER_AND_MASK) 308 | content: 4 bytes, register content 309 | response : - 310 | """ 311 | def writeRegisterMultiple(self, address, parameterList): 312 | parameters = [] 313 | parameters.insert(0, address) 314 | for param in parameterList: 315 | parameters.extend(param[0]) 316 | parameters.extend(param[1]) 317 | parameters.extend(self._toList(param[2])) 318 | 319 | return self._sendCommand(self.CMD['WRITE_REGISTER_AND_MASK'], parameters, 0) 320 | 321 | """ 322 | readRegister(self, address) 323 | address : 1 byte, Register address 324 | response : 4 bytes, register content 32-bit value (little endian). 325 | """ 326 | def readRegister(self, address): 327 | parameters = [] 328 | parameters.insert(0, address) 329 | regList = self._sendCommand(self.CMD['READ_REGISTER'], parameters, 4) 330 | return self._toInt32(regList) 331 | 332 | 333 | """ 334 | readRegisterMultiple(self, addressList) 335 | addressList : 1 to 18 bytes, Register address list 336 | response : 4 to 72 bytes, register content 32-bit value (little endian). 337 | """ 338 | def readRegisterMultiple(self, addressList): 339 | parameters = [] 340 | for param in addressList: 341 | parameters.extend(param) 342 | return self._sendCommand(self.CMD['READ_REGISTER_MULTIPLE'], parameters, 4*len(addressList)) 343 | 344 | 345 | """ 346 | TODO 347 | writeEeprom(self) 348 | """ 349 | # def readEeprom(self, address, length): 350 | # parameters = [] 351 | # parameters.insert(0, address) 352 | # parameters.insert(1, length) 353 | # return self._sendCommand(self.CMD['READ_EEPROM'], parameters, length) 354 | 355 | 356 | """ 357 | readEeprom(self, address, length) 358 | After this instruction has been executed, an RF transmission can be started by configuring the corresponding registers 359 | Address: 1 byte, Address in EEPROM from which read operation starts (EEPROM Address) 360 | length : 1 byte, Number of bytes to read from EEPROM 361 | """ 362 | def readEeprom(self, address, length): 363 | parameters = [] 364 | parameters.insert(0, address) 365 | parameters.insert(1, length) 366 | return self._sendCommand(self.CMD['READ_EEPROM'], parameters, length) 367 | 368 | 369 | """ 370 | writeData(self, parameterList) 371 | parameterList: 1 to 260 bytes, data to transmit (written to transmit buffer) 372 | response : - 373 | """ 374 | def writeData(self, parameterList): 375 | parameters = [] 376 | for param in parameterList: 377 | parameters.extend(param) 378 | return self._sendCommand(self.CMD['WRITE_DATA'], parameters, 0) 379 | 380 | 381 | """ 382 | sendData(self, numberOfValidBits, parameterList) 383 | numberOfValidBits : 1 byte, Number of valid bits in last Byte 384 | parameterList: 1 to 260 bytes, data to transmit (written to transmit buffer) 385 | response : - 386 | """ 387 | def sendData(self, numberOfValidBits, parameterList): 388 | parameters = [] 389 | parameters.insert(0, numberOfValidBits) 390 | for param in parameterList: 391 | parameters.append(param) 392 | return self._sendCommand(self.CMD['SEND_DATA'], parameters, 0) 393 | 394 | 395 | """ 396 | readData(self, len) 397 | len : 1 to 508 bytes 398 | response : 1 to 508 bytes read from Rx buffer 399 | """ 400 | def readData(self, len): 401 | parameters = [] 402 | parameters.insert(0, 0) 403 | return self._sendCommand(self.CMD['READ_DATA'], parameters, len) 404 | 405 | 406 | """ 407 | switchMode(self) 408 | TODO 409 | response : - 410 | """ 411 | # def switchMode(self): 412 | # parameters = [] 413 | # parameters.insert(0, 0) 414 | # return self._sendCommand(self.CMD['READ_DATA'], parameters, 0) 415 | 416 | 417 | """ 418 | loadRfConfig(self, txCfg, rxCfg) 419 | This instruction is used to load the RF configuration from EEPROM into the configuration registers. 420 | txCfg : 1 byte, Transmitter configuration byte 421 | rxCfg : 1 byte, receiver configuration byte 422 | response : - 423 | """ 424 | def loadRfConfig(self, txCfg, rxCfg): 425 | parameters = [] 426 | parameters.insert(0, txCfg) 427 | parameters.insert(1, rxCfg) 428 | return self._sendCommand(self.CMD['LOAD_RF_CONFIG'], parameters, 0) 429 | 430 | 431 | """ 432 | rfOn(self, ctrl) 433 | ctrl : 1 byte, 434 | Bit0 == 1: disable collision avoidance according to ISO/IEC 18092 435 | Bit1 == 1: Use Active Communication mode according to ISO/IEC 18092 436 | response : - 437 | """ 438 | def rfOn(self, ctrl): 439 | parameters = [] 440 | parameters.insert(0, ctrl) 441 | return self._sendCommand(self.CMD['RF_ON'], parameters, 0) 442 | 443 | 444 | """ 445 | rfOff(self) 446 | response : - 447 | """ 448 | def rfOff(self): 449 | parameters = [] 450 | parameters.insert(0, 0) 451 | return self._sendCommand(self.CMD['RF_OFF'], parameters, 0) 452 | 453 | --------------------------------------------------------------------------------