Read and write NFC tags in Python!
Hands-on Utilization of Raspberry Pi for Industrial Applications

I have the impression that RFID-based business management has become widespread in all industries. Perhaps it was around 2010, when the initial cost was much higher than now, but the introduction of RFID was noticeable among major companies and certain industries.

In response to the challenges facing Japanese society, there is a need to automate and improve the efficiency of business operations.
In terms of labor shortages and human resources, if companies want to improve efficiency, AI technology will also be introduced in the future.

When considering efficiency, the era of data collection, accumulation, and analysis has passed. In the future, there is a growing need for further optimization, including forecasting and automation, based on data.

Among them, the system using RFID tags is recommended as the first approach because it is less difficult to introduce and has a wide range of applicability to various cases.

What is RFID?

Electronic payments, which have already become commonplace, are another type of RFID.
Many people find it very difficult to understand the many terms involved in RFID.

RFID stands for “Radio Frequency Identification,” which translates directly to “radio frequency identification,” and is a system that uses radio waves to receive data contactlessly.

It is a radio wave and has a bandwidth.
For example, NFC used for electronic payment is in the HF band (short wave: 13.56 MHz), and is a mechanism for exchanging data over a short distance.
NFC stands for “Near Field Communication,” so it means “short-range wireless communication,” and short distance is the term.
NFC can be paraphrased as the HF band of RFID.

UHF band

The other band is the UHF band.
UHF stands for “Ultra High Frequency. The 920 MHz band in Japan and the 860 MHz band overseas are the most common.

This band is mainly used in industrial applications. This is because of its advantages in industrial applications.

Examples of industrial RFID tag products

  • Attachable to metal surfaces
  • Can be embedded in concrete
  • completely waterproof
  • Readable even on fast moving objects.

The aforementioned HF-band NFC specifications can only be read at a distance of about 10 cm.
Imagine an electronic payment. It is generally that distance.

On the other hand, the UHF band can be read from several meters to 5 meters away. *Distances in both cases vary depending on conditions.
Additionally, while NFC (≒HF band) is a one-to-one communication, UHF band can read several at once.

RFID for business use

The easiest way to imagine a system using RFID is product management.
This can be an effective means of picking up goods at distribution centers and managing inventory and shipping/receiving at warehouses.

In factories, it would be useful for process control and quality control.
The system could be used for inventory control of semi-finished products to prevent overstocking and shortages of finished products.

In the past, we went from paper-based operations to barcode management, and then QR code management became common. Now RFID, especially UHF, systems that scan multiple items at the same time are common.

Widespread QR codes, for example, are vulnerable to dirt and obstructions, and often cannot be used at the sites they are intended for.
In this respect, RFID tags can be adapted to any site because of their resistance to dirt and obstacles.

Initial and running costs are lower than before.
Space-saving systems, such as the industrial Raspberry Pi, can now be installed without directly connecting to large PCs.
IoT terminals that work together have also improved in processing speed over the past few years.

This environment has also increased what can be done to systemize the use of RFID tags.
 

UHF tag reading system (PL-R4 example)

RFID and NFC in a nutshell include very detailed and complex content, so you want to choose a device that is specialized for industrial use from the beginning.

On the industrial Raspberry Pi, it is not unreasonable to systemize an RFID reader using NFC or UHF bands. It takes up very little space and is very compact.
A small monitor can be attached for real-time visualization.

The Raspberry Pi itself is also designed for industrial applications, so it can be installed in harsh environments.

Try NFC on an industrial Raspberry Pi

From here, we will try out the actual operation of the RFID tag.
It is recommended to actually run the system to better understand the loading speed, etc.

If NFC (≈HF band) instead of UHF band, you can try it right at hand.

Even if you have to test a sample program or code it yourself, you can now use ChatGPT to prepare a program with little effort.

We also tested reading and writing on an industrial Raspberry Pi using a readily available commercial product for read-writers that read NFC tags.

A working Python program is included in the latter half of the document, so please copy and use it.

Environment of this time

The Raspberry Pi OS (bullseye) is installed on the PL-R4 (using CM4), an industrial Raspberry Pi. 32-bit version.

PL-R4 BASIC RJ Plus IP20 [sample machine].
NFC Card Reader
  • PL-R4 (with Raspberry Pi CM4)
  • Raspberry Pi OS (bullseye) 32bit
  • Sony PaSoRi S380/P
  • Two types of NFC cards for read/write

Sony S380/P can confirm the required ID by connecting with a USB cable and checking with lsusb.

lsusb

Bus 001 Device 008: ID 054c:06c3 Sony Corp. RC-S380

Install nfcpy

A useful package for dealing with NFC readers on the Raspberry Pi is nfcpy.
Let’s install nfcpy first and try it from a sample program.

I want to install it with pip, so I made Python a virtual environment and then installed it.

source env/bin/activatethen you will see (env) in the terminal. This tells you that you are in a virtual environment.

python3 -m venv env     #Python仮想環境
source env/bin/activate #仮想環境をアクティブに
pip3 install nfcpy #pip3でインストール

Finally, if you want to go back to the original prompt from (env), just type deactivateand run it to get back to the original. In this case, we will stay in (env) until everything is finished.

When nfcpy was installed, the version, including other modules, was as follows (at time of writing)

libusb1-3.3.1 ndeflib-0.3.3 nfcpy-1.0.4 pydes-2.0.1 pyserial-3.5

All that remains is to check the operation and then execute the program to read/write.

operation check

To begin, run python3 -m nfc. The final result should look something like this

python3 -m nfc

This is the 1.0.4 version of nfcpy run in Python 3.9.2
on Linux-6.1.21-v8+-aarch64-with-glibc2.31
I'm now searching your system for contactless devices
** found SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
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

At first it will probably be an error.

This is because the NFC reader is running with user privileges, even though the device operates with root privileges.
You can run it with sudo, but add the udev rule as per the terminal message.
The command is displayed, so copy and execute it as is, and you are done.

The following example is my environment.
The Vendor and Product values are different for each device. Do not copy them as is. Run the command as it appears in the terminal in your environment.

For Sony S380/P, 054cand 06c3are the IDs.

sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'

sudo udevadm control -R #即時有効化

Now you are ready to go.
Reboot here once.

After rebooting, it is no longer necessary to run with sudo privileges
Run python3 -m nfcagain and check for errors as described at the beginning.

Thereafter, Python programs dealing with NFC readers can also be run without sudo.

After reboot, run source env/bin/activateand then run the sample program.
If you just ran python3 -m venv envin your home directory, it is source ~/env/bin/activate.
If you do not have a venv environment, you will get the error ModuleNotFoundError: No module named 'ndef'.

Try a sample

Get the nfcpy sample files via git clone. The files are located at /nfcpy/examples.

git clone https://github.com/nfcpy/nfcpy.git
cd ./nfcpy/examples

Among several, we will use tagtool.py to retrieve the information.

tagtool.py is a program that can also read, write, and format.
Add a show command to view the contents, and format to format (initialize).

fetch (e.g. CPU inst.)

python3 tagtool.py show

Formatting (initialization)

python3 tagtool.py format

Once data is written to NFC, it can be locked. If the lock is not applied, the content can be erased and written anew. It is estimated that the data can be rewritten approximately 100,000 times. This is sufficient.

Only the commands to write are a bit complicated in tagtool.py, so we will ask you to write a program to write to ChatGPT later.

The first card we tried was NXP NTAG215 in plain white.

Here is a view of the contents by tagtool.py.

python3 tagtool.py show

[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
** waiting for a tag **
Type2Tag 'NXP NTAG215' ID=04C154B2C11190
NDEF Capabilities:
  readable  = yes
  writeable = yes
  capacity  = 492 byte
  message   = 0 byte

The tag type is Type2Tag, the capacity is 492 bytes for read/write, and the NDEF Message is still 0 bytes, so it is empty.

There are several types of tags, for example, Type3Tag is the famous FeliCa for electronic money. In this case, it is Type2Tag.

Next, try another card to which data had been written.

[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
** waiting for a tag **
Type2Tag 'NXP NTAG215' ID=04A32EB2C11190
NDEF Capabilities:
  readable  = yes
  writeable = yes
  capacity  = 492 byte
  message   = 117 byte
NDEF Message:
record 1
  type = 'urn:nfc:wkt:U'
  name = ''
  data = b'\x04raspida.com'
record 2
  type = 'urn:nfc:wkt:T'
  name = ''
  data = b'\x02en\xe3\x83\xa9\xe3\x82\xba\xe3\x83\x91\xe3\x82\xa4\xe3\x83\x80\xe3\x81\xaf\xe9\x9d\x9e\xe3\x82\xa8\xe3\x83\xb3\xe3\x82\xb8\xe3\x83\x8b\xe3\x82\xa2\xe3\x81\xa7\xe3\x82\x82\xe6\xa5\xbd\xe3\x81\x97\xe3\x82\x81\xe3\x82\x8bRaspberry Pi \xe3\x82\x92\xe7\xb4\xb9\xe4\xbb\x8b\xe3\x81\x97\xe3\x81\xa6\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82'

There are two records under NDEF Message and something in the form of data.
record 1 is the URL of the site, and record 2 is a string of Unicode (industry standard) characters encoded in UTF-8.

The first “\x02” in record 2 is a control character (STX).
The following characters are UTF-8 characters, where one byte is one character for English (ASCII) and three bytes are one character for Japanese (Unicode).

| Japanese | Unicode | UTF-8 encoded |
| :-: | :—–: | :———-: |
| La | U+30E9 | \xe3\x83\xa9

What is actually written is the subsequent string, excluding the start sign = “\x02en”.

Binary Data Conversion

I was curious, though not particularly necessary, so I asked ChatGPT to create code in Python to convert (decode) Unicode characters encoded in UTF-8 back to text.

The “\x02en” is removed to make it easier to read, since it comes on its own when using a lead writer.

decode_binary.py

# バイナリ文字列(UnicodeのUTF-8)を標準入力から受け取るスクリプト

def main():
    # 入力を受け取る(例: \x02en\xe3\x83... のような文字列)
    raw_input = input("バイナリ文字列を入力してください(例: \\x02en...):\n")

    try:
        # 文字列をバイト列に変換(エスケープシーケンスとして解釈)
        byte_data = raw_input.encode('utf-8').decode('unicode_escape').encode('latin1')

        # 先頭3バイト (\x02 + 'e' + 'n') を取り除く
        if byte_data.startswith(b'\x02en'):
            byte_data = byte_data[3:]

        # UTF-8としてデコード
        decoded_text = byte_data.decode('utf-8')

        print("\n変換結果:")
        print(decoded_text)

    except Exception as e:
        print("\nエラーが発生しました:")
        print(e)

if __name__ == "__main__":
    main()

Conversion results:
https://raspida.com
Raspida introduces Raspberry Pi for non-engineers to enjoy.

Now you can read it when you convert it.

store

The next step is to write.
Here is the Python program I asked ChatGPT for again.

As a condition, the data types to be written are narrowed down to three, which can be selected and written.
Only one record can be written, and multiple records are not supported.

  • URI
  • text
  • vCard (business card)

A URI is a data format in which a URL is written; when a URL is written to an NFC tag, it can be read directly by a smartphone. The associated web browser will launch.

Other formats currently do not do anything when you try to read them with your phone. You need a separate app to read NFC tags.

The data format of the vCard should be in accordance with the vCard 3.0 specification. In this case, since the NFC tag was only 137 bytes, there was not enough space, so a timestamp such as REV was not set.

This is a sticker type NFC tag like this one: NXP NTAG213.

write_nfc.py

import nfc
import ndef

def write_tag(tag, data_type, content, language, name=None, last_name=None, first_name=None):
    if tag.ndef is None:
        print("❌ このタグはNDEFに対応していません。")
        return False

    if data_type == "uri":
        records = [ndef.UriRecord(content)]
    elif data_type == "text":
        records = [ndef.TextRecord(content, language=language)]
    elif data_type == "vcard":
        # vCardフォーマットにnameを空文字列、last_nameとfirst_nameを含める
        vcard_text = f"BEGIN:VCARD\nVERSION:3.0\nN:{last_name};{first_name}\nTEL:{content}\nEND:VCARD"
        records = [ndef.Record(type="text/vcard", name="", data=vcard_text.encode('utf-8'))]
    else:
        print(f"❌ データタイプ '{data_type}' は未対応です。")
        return False

    tag.ndef.records = records
    print("✅ 書き込み完了!")
    return True

def main():
    print("データタイプを選んでください:")
    print("  1: URI")
    print("  2: Text")
    print("  3: vCard")
    choice = input("番号を入力してください (1, 2, 3): ")

    language = "ja"  # 常に日本語に設定

    if choice == "1":
        data_type = "uri"
        content = input("URIを入力してください: ")
        name = None  # URIの場合、nameは不要
        last_name = first_name = None
    elif choice == "2":
        data_type = "text"
        content = input("テキストを入力してください: ")
        name = None  # Textの場合、nameは不要
        last_name = first_name = None
    elif choice == "3":
        data_type = "vcard"
        name = ""  # vCardのnameは空文字
        last_name = input("姓 (例: 山田): ")
        first_name = input("名 (例: 太郎): ")
        content = input("電話番号 (例: 0312345678): ")
    else:
        print("無効な選択肢です。終了します。")
        return

    print("カードをタッチしてください...")
    try:
        clf = nfc.ContactlessFrontend('usb')
        clf.connect(rdwr={'on-connect': lambda tag: write_tag(tag, data_type, content, language, name,>
    except Exception as e:
        print(f"❌ NFCリーダーに接続できませんでした: {e}")

if __name__ == "__main__":
    main()

They are waiting to be entered one at a time at the terminal.
After writing the text, check each one to see if it was written by tagtool.py.

Input example: 1

データタイプを選んでください:
  1: URI
  2: Text
  3: vCard
番号を入力してください (1, 2, 3): 1
URIを入力してください: http://raspida.com/
カードをタッチしてください...
✅ 書き込み完了!

Result of tagtool.py show:

[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
** waiting for a tag **
Type2Tag 'NXP NTAG213' ID=0420B2C24C5880
NDEF Capabilities:
  readable  = yes
  writeable = yes
  capacity  = 137 byte
  message   = 17 byte
NDEF Message:
record 1
  type = 'urn:nfc:wkt:U'
  name = ''
  data = b'\x03raspida.com/'

Input example: 2

データタイプを選んでください:
  1: URI
  2: Text
  3: vCard
番号を入力してください (1, 2, 3): 2
テキストを入力してください: 書き込みのテストです。
カードをタッチしてください...
✅ 書き込み完了!

Result of tagtool.py show:

[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
** waiting for a tag **
Type2Tag 'NXP NTAG213' ID=0420B2C24C5880
NDEF Capabilities:
  readable  = yes
  writeable = yes
  capacity  = 137 byte
  message   = 40 byte
NDEF Message:
record 1
  type = 'urn:nfc:wkt:T'
  name = ''
  data = b'\x02ja\xe6\x9b\xb8\xe3\x81\x8d\xe8\xbe\xbc\xe3\x81\xbf\xe3\x81\xae\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe3\x81\xa7\xe3\x81\x99\xe3\x80\x82'

Input example: 3

データタイプを選んでください:
  1: URI
  2: Text
  3: vCard
番号を入力してください (1, 2, 3): 3
姓 (例: 山田): 山田
名 (例: 太郎): 太郎
電話番号 (例: 0312345678): 0312345678
カードをタッチしてください...
✅ 書き込み完了!

Result of tagtool.py show:

python3 tagtool.py show

[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:008
** waiting for a tag **
Type2Tag 'NXP NTAG215' ID=04DE79B2C11190
NDEF Capabilities:
  readable  = yes
  writeable = yes
  capacity  = 492 byte
  message   = 77 byte
NDEF Message:
record 1
  type = 'text/vcard'
  name = ''
  data = bytearray(b'BEGIN:VCARD\nVERSION:3.0\nN:\xe5\xb1\xb1\xe7\x94\xb0;\xe5\xa4\xaa\xe9\x83\x8e\nTEL:0312345678\nEND:VCARD')

Data was written correctly.

Reading and writing NFC tags in this way could be achieved with a Python program.
Even I, a non-engineer, can prepare a program that works without errors using ChatGPT.
Based on this code, try modifying it to other data formats, multiple records, etc.


Reference
https://nfcpy.readthedocs.io/en/latest/topics/get-started.html
See also https://vcardmaker.com


Article contributed by Raspida

Raspberry Pi information site that even non-engineers can enjoy using raspida.com a Raspberry Pi information site that even non-engineers can enjoy and handle. He also contributes technical blog articles to the PiLink site on the Raspberry Pi for industrial use.