├── 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 |
--------------------------------------------------------------------------------