Getting started¶
Installation¶
NFCPy requires the library libusb for generic access to USB devices.
Install libusb (Linux)
Linux distributions usually have this installed, otherwise it should be
available through the standard package manager (beware not to choose
the old version 0.x
).
Install libusb (Windows)
Windows users will have to manually install WinUSB and libusb. Microsoft provides instructions to install WinUSB but a much simpler approach is to use Zadig (a driver installation helper application).
- Download Zadig
- Run the standalone exe
- Click Options -> List All Devices
- Select your NFC reading/writing device from the list
- Select the WinUSB driver from the other drop down and install it
Then, install libusb:
- Download libusb (Downloads -> Latest Windows Binaries).
- Unpack the 7z archive (you may use 7zip).
- For 32-bit Windows:
- Copy
MS32\dll\libusb-1.0.dll
toC:\Windows\System32
.
- Copy
- For 64-bit Windows:
- Copy
MS64\dll\libusb-1.0.dll
toC:\Windows\System32
. - Copy
MS32\dll\libusb-1.0.dll
toC:\Windows\SysWOW64
.
- Copy
Install Python and nfcpy
Download and install the latest version of Python 2.7.x ( nfcpy does not support Python 3 (yet) [1]).
Note
Python may already be installed on your system if you are a Linux user.
Once Python is installed use pip to install the latest stable
version of nfcpy. This will also install the required libusb1
and pyserial
Python modules.
$ pip install -U nfcpy
Windows users will want to ensure they have configured their
environment’s PATH correctly, otherwise they will not be able to
access pip
on the command line. It is usually located at
C:\Python27\Scripts\pip.exe
so they must ensure
C:\Python27\Scripts\
is on their PATH.)
Verify installation
Check if everything installed correctly and that nfcpy is able to find your contactless reader.
$ python -m nfc
If all goes well the output should tell that your your reader was found, below is an example of how it may look with an SCL3711:
This is the latest version of nfcpy run in Python 2.7.12
on Linux-4.4.0-47-generic-x86_64-with-Ubuntu-16.04-xenial
I'm now searching your system for contactless devices
** found SCM Micro SCL3711-NFC&RW PN533v2.7 at usb:002:024
I'm not trying serial devices because you haven't told me
-- add the option '--search-tty' to have me looking
-- but beware that this may break existing connections
Common problems on Linux (access rights or other drivers claiming the device) should be reported with a possible solution:
This is the latest version of nfcpy run in Python 2.7.12
on Linux-4.4.0-47-generic-x86_64-with-Ubuntu-16.04-xenial
I'm now searching your system for contactless devices
** found usb:04e6:5591 at usb:002:025 but access is denied
-- the device is owned by 'root' but you are 'stephen'
-- also members of the 'root' group would be permitted
-- you could use 'sudo' but this is not recommended
-- it's better to add the device to the 'plugdev' group
sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"04e6\", ATTRS{idProduct}==\"5591\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
sudo udevadm control -R # then re-attach device
I'm not trying serial devices because you haven't told me
-- add the option '--search-tty' to have me looking
-- but beware that this may break other serial devs
Sorry, but I couldn't find any contactless device
Open a local device¶
Any data exchange with a remote NFC device needs a contactless frontend attached and opened for communication. Most commercial devices (also called NFC Reader) are physically attached through USB and either provide a native USB interface or a virtual serial port.
The nfc.ContactlessFrontend
manages all communication with a local
device. The open
method tries to find and
open a device and returns True for success. The string argument determines the
device with a sequence of components separated by colon. The first component
determines where the device is attached (usb, tty, or udp) and what the further
components may be. This is best explained by example.
Suppose a FeliCa S330 Reader is attached to a Linux computer on USB bus number 3 and got device number 9 (note that device numbers always increment when a device is connected):
$ lsusb
...
Bus 003 Device 009: ID 054c:02e1 Sony Corp. FeliCa S330 [PaSoRi]
...
>>> import nfc
>>> clf = nfc.ContactlessFrontend()
>>> assert clf.open('usb:003:009') is True # open device 9 on bus 3
>>> assert clf.open('usb:054c:02e1') is True # open first PaSoRi 330
>>> assert clf.open('usb:003') is True # open first Reader on bus 3
>>> assert clf.open('usb:054c') is True # open first Sony Reader
>>> assert clf.open('usb') is True # open first USB Reader
>>> clf.close() # previous open calls implicitly closed the device
Some devices, especially for embedded projects, have a UART interface that may be connected either directly or through a USB UART adapter. Below is an example of a Raspberry Pi 3 which has two UART ports (ttyAMA0, ttyS0) and one reader is connected with a USB UART adapter (ttyUSB0). On a Raspberry Pi 3 the UART linked from /dev/serial1 is available on the GPIO header (the other one is used for Bluetooth connectivity). On a Raspberry Pi 2 it is always ttyAMA0.
pi@raspberrypi ~ $ ls -l /dev/tty[ASU]* /dev/serial?
lrwxrwxrwx 1 root root 5 Dez 21 18:11 /dev/serial0 -> ttyS0
lrwxrwxrwx 1 root root 7 Dez 21 18:11 /dev/serial1 -> ttyAMA0
crw-rw---- 1 root dialout 204, 64 Dez 21 18:11 /dev/ttyAMA0
crw-rw---- 1 root dialout 4, 64 Dez 21 18:11 /dev/ttyS0
crw-rw---- 1 root dialout 188, 0 Feb 24 12:17 /dev/ttyUSB0
>>> import nfc
>>> clf = nfc.ContactlessFrontend()
>>> assert clf.open('tty:USB0:arygon') is True # open /dev/ttyUSB0 with arygon driver
>>> assert clf.open('tty:USB0:pn532') is True # open /dev/ttyUSB0 with pn532 driver
>>> assert clf.open('tty:AMA0') is True # try different drivers on /dev/ttyAMA0
>>> assert clf.open('tty') is True # try all serial ports and drivers
>>> clf.close() # previous open calls implicitly closed the device
A special kind of device bus that does not require any physical hardware is provided for testing and application prototyping. It works by sending NFC communication frames across a UDP/IP connection and can be used to connect two processes running an nfcpy application either locally or remote.
In the following example the device path is supplied as an init argument. This
would raise an exceptions.IOError
with errno.ENODEV
if it fails
to open. The example also demonstrates the use of a with
statement
for automatic close when leaving the context.
>>> import nfc
>>> with nfc.ContactlessFrontend('udp') as clf:
... print(clf)
...
Linux IP-Stack on udp:localhost:54321
Read and write tags¶
NFC Tag Devices are tiny electronics devices with a comparatively large (some square centimeters) antenna that serves as both an inductive power receiver and for communication. The energy is provided by the NFC Reader Device for as long as it wishes to communicate with the Tag.
Most Tags are embedded in plastics or paper and can store data in persistent memory. NFC Tags as defined by the NFC Forum have standardized memory format and command set to store NFC Data Exchange Format (NDEF) records. Most commercial NFC Tags also provide vendor-specific commands for special applications, some of those can be used with nfcpy. A rather new class of NFC Interface Tags is targeted towards providing NFC communication for embedded devices where the information exchange is through NFC with the microcontroller of the embedded device.
Tip
It is quite easy to make an NFC field detector. Just a few turns of copper wire around three fingers and the ends soldered to an LED will do the job. Here’s a video.
NFC Tags are simple slave devices that wait unconditionally for any reader command to respond. This makes it easy to interact with them from within a Python interpreter session using the local contactless frontend.
>>> import nfc
>>> clf = nfc.ContactlessFrontend('usb')
The clf.sense()
method can now be used to search for a proximity target with
arguments set for the desired communication technologies. The example shows the
result of a Type F card response for which the nfc.tag.activate()
function
then returns a Type3Tag
instance.
>>> from nfc.clf import RemoteTarget
>>> target = clf.sense(RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F'))
>>> print(target)
212F sensf_res=0101010701260CCA020F0D23042F7783FF12FC
>>> tag = nfc.tag.activate(clf, target)
>>> print(tag)
Type3Tag 'FeliCa Standard (RC-S960)' ID=01010701260CCA02 PMM=0F0D23042F7783FF SYS=12FC
The same Type3Tag
instance can also be acquired with the
clf.connect()
method. This is the generally preferred way to discover and
activate contactless targets of any supported type. When configured with the
rdwr dictionary argument the clf.connect()
method will use Reader/Writer mode
to discover NFC Tags. When a Tag is found and activated, the on-connect
callback function returning False
means that the tag presence loop
shall not be run but the nfc.tag.Tag
object returned immediately. A
more useful callback function could do something with the tag and return
True
for requesting a presence loop that makes clf.connect()
return
only after the tag is gone.
>>> tag = clf.connect(rdwr={'on-connect': lambda tag: False})
>>> print(tag)
Type3Tag 'FeliCa Standard (RC-S960)' ID=01010701260CCA02 PMM=0F0D23042F7783FF SYS=12FC
An NFC Forum Tag can store NFC Data Exchange Format (NDEF) Records in a
specifically formatted memory region. NDEF data is found automatically and
wrapped into an NDEF
object accessible through the
tag.ndef
attribute. When NDEF data is not present the attribute is simply
None
.
>>> assert tag.ndef is not None
>>> for record in tag.ndef.records:
... print(record)
...
NDEF Uri Record ID '' Resource 'http://nfcpy.org'
The tag.ndef.records
attribute contains a list of NDEF Records decoded from
tag.ndef.octets
with the ndeflib package. Each record has common and
type-specific methods and attributes for content access.
>>> record = tag.ndef.records[0]
>>> print(record.type)
urn:nfc:wkt:U
>>> print(record.uri)
http://nfcpy.org
A list of NDEF Records assigned to tag.ndef.records
gets encoded and then
written to the Tag (internally the bytes are assigned to tag.ndef.octets
to
trigger the update).
>>> import ndef
>>> uri, title = 'http://nfcpy.org', 'nfcpy project'
>>> tag.ndef.records = [ndef.SmartposterRecord(uri, title)]
When NDEF data bytes are written to a Memory Tag then the tag.ndef
object
matches the stored data. In case of an Interface Tag this may not be true
because the write commands may be handled differently by the device. The only
way to find out is read back the data and compare. This is the logic behind
tag.ndef.has_changed
, which should be False
for a Memory Tag.
>>> assert tag.ndef.has_changed is False
An NFC Interface Tag may be used to realize a device that presents dynamically changing NDEF data depending on internal state, for example a sensor device returning the current temperature.
>>> tag = clf.connect(rdwr={'on-connect': lambda tag: False})
>>> print(tag)
Type3Tag 'FeliCa Link (RC-S730) Plug Mode' ID=03FEFFFFFFFFFFFF PMM=00E1000000FFFF00 SYS=12FC
>>> assert tag.ndef is not None and tag.ndef.length > 0
>>> assert tag.ndef.records[0].type == 'urn:nfc:wkt:T'
>>> print('Temperature 0: {}'.format(tag.ndef.records[0].text))
Temperature 0: +21.3 C
>>> for count in range(1, 4):
... while not tag.ndef.has_changed: time.sleep(1)
... print('Temperature {}: {}'.format(count, tag.ndef.records[0].text))
...
Temperature 1: +21.0 C
Temperature 2: +20.5 C
Temperature 3: +20.1 C
Finally the contactless frontend should be closed.
>>> clf.close()
Documentation of all available Tag classes as well as NDEF class methods and
attributes can be found in the nfc.tag
module reference. For NDEF Record
class types, methods and attributes consult the ndeflib documentation.
Emulate a card¶
It is possible to emulate a card (NFC Tag) with nfcpy but unfortunately this only works with some NFC devices and is limited to Type 3 Tag emulation. The RC-S380 fully supports Type 3 Tag emulation. Devices based on PN532, PN533, or RC-S956 chipset can also be used but an internal frame size limit of 64 byte only allows read/write operations with up to 3 data blocks.
Below is an example of an NDEF formatted Type 3 Tag. The first 16 byte (first data block) contain the attribute data by which the reader will learn the NDEF version, the number of data blocks that can be read or written in a single command, the total capacity and the write permission state. Bytes 11 to 13 contain the current NDEF message length, initialized to zero. The example is made to specifically open only an RC-S380 contactless frontend (otherwise the number of blocks that may be read or written should not be more than 3).
import nfc
import struct
ndef_data_area = bytearray(64 * 16)
ndef_data_area[0] = 0x10 # NDEF mapping version '1.0'
ndef_data_area[1] = 12 # Number of blocks that may be read at once
ndef_data_area[2] = 8 # Number of blocks that may be written at once
ndef_data_area[4] = 63 # Number of blocks available for NDEF data
ndef_data_area[10] = 1 # NDEF read and write operations are allowed
ndef_data_area[14:16] = struct.pack('>H', sum(ndef_data_area[0:14])) # Checksum
def ndef_read(block_number, rb, re):
if block_number < len(ndef_data_area) / 16:
first, last = block_number*16, (block_number+1)*16
block_data = ndef_data_area[first:last]
return block_data
def ndef_write(block_number, block_data, wb, we):
global ndef_data_area
if block_number < len(ndef_data_area) / 16:
first, last = block_number*16, (block_number+1)*16
ndef_data_area[first:last] = block_data
return True
def on_startup(target):
idm, pmm, sys = '03FEFFE011223344', '01E0000000FFFF00', '12FC'
target.sensf_res = bytearray.fromhex('01' + idm + pmm + sys)
target.brty = "212F"
return target
def on_connect(tag):
print("tag activated")
tag.add_service(0x0009, ndef_read, ndef_write)
tag.add_service(0x000B, ndef_read, lambda: False)
return True
with nfc.ContactlessFrontend('usb:054c:06c1') as clf:
while clf.connect(card={'on-startup': on_startup, 'on-connect': on_connect}):
print("tag released")
This is a fully functional NFC Forum Type 3 Tag. With a separate reader or Android apps such as NXP Tag Info and NXP Tag Writer, NDEF data can now be written into the ndef_data_area and read back until the loop is terminated with Control-C.
Work with a peer¶
The best part of NFC comes when the limitations of a single master controlling a humble servant are overcome. This is achieved by the NFC Forum Logical Link Control Protocol (LLCP), which allows multiplexed communications between two NFC Forum Devices with either peer able to send protocol data units at any time and no restriction to a single application run in one direction.
An LLCP link between two NFC devices is requested with the llcp
argument to clf.connect()
.
>>> import nfc
>>> clf = ContactlessFrontend('usb')
>>> clf.connect(llcp={}) # now touch a phone
True
When the first example got LLCP running there is actually just symmetry packets exchanged back and forth until the link is broken. We have to use callback functions to add some useful stuff.
>>> def on_connect(llc):
... print llc; return True
...
>>> clf.connect(llcp={'on-connect': connected})
LLC: Local(MIU=128, LTO=100ms) Remote(MIU=1024, LTO=500ms)
True
The on_connect function receives a single argument llc, which is
the LogicalLinkController
instance coordinates
aal data exchange with the remote peer. With this we can add client
applications but they must be run in a separate execution context to
have on_connect return fast. Only after on_connect returns, the
llc can start running the symmetry loop (the LLCP heartbeat) with
the remote peer and generally receive and dispatch protocol and
service data units.
When using the interactive interpreter it is less convinient to
program in the callback functions so we will start a thread in the
callback to execute the llc.run* loop and return with False. This
tells clf.connect()
to return immediately with the llc instance).
>>> import threading
>>> def on_connect(llc):
... threading.Thread(target=llc.run).start(); return False
...
>>> llc = clf.connect(llcp={'on-connect': on_connect})
>>> print llc
LLC: Local(MIU=128, LTO=100ms) Remote(MIU=1024, LTO=500ms)
Application code is not supposed to work directly with the llc
object but use it to create Socket
objects for the
actual communication. Two types of regular sockets can be created with
either nfc.llcp.LOGICAL_DATA_LINK
for a connection-less
socket or nfc.llcp.DATA_LINK_CONNECTION
for a connection-mode
socket. A connection-less socket does not guarantee that application
data is delivered to the remote application (although nfcpy makes
sure that it’s been delivered to the remote device). A connection-mode
socket cares about reliability, unless the other implementation is
buggy data you send is guaranteed to make it to the receiving
application - error-free and in order.
What can be done with an Android phone as the peer device is for example to send to its default SNEP Server. SNEP is the NFC Forum Simple NDEF Exchange Protocol and a default SNEP Server is built into Android under the name of Android Beam. SNEP messages are exchanged over an LLCP data link connection so we create a connection mode socket, connect to the server with the service name known from the NFC Forum Assigned Numbers Register and then send a SNEP PUT request with a web link to open.
>>> import ndef
>>> socket = nfc.llcp.Socket(llc, nfc.llcp.DATA_LINK_CONNECTION)
>>> socket.connect('urn:nfc:sn:snep')
>>> records = [ndef.UriRecord("http://nfcpy.org")]
>>> message = b''.join(ndef.message_encoder(records))
>>> socket.send("\x10\x02\x00\x00\x00" + chr(len(message)) + message)
>>> socket.recv()
'\x10\x81\x00\x00\x00\x00'
>>> socket.close()
The phone should now have opened the http://nfcpy.org web page.
The code can be simplified by using the SnepClient
from the nfc.snep
package.
>>> import nfc.snep
>>> snep = nfc.snep.SnepClient(llc)
>>> snep.put_records([ndef.UriRecord("http://nfcpy.org")])
True
The put()
method is smart enough to
temporarily connect to urn:nfc.sn:snep
for sending. There are also
methods to open and close the connection explicitely and maybe use a
different service name.
Note
The Logical Link Control Protocol tutorial has more information on
LLCP in general and how its used with nfcpy. The
nfc.llcp
package documentation contains describes all
the API classes and methods that are available.
[1] | https://github.com/nfcpy/nfcpy/issues/47 |