用 Python 读写 NFC 标签!
工业应用中的 Raspberry Pi 实践应用

在我的印象中,基于 RFID 的企业管理已在各行各业普及。也许是在 2010 年前后,当时的初始成本要比现在高得多,但在大公司和某些行业中,RFID 的引入还是很明显的。

为应对日本社会面临的挑战,有必要实现企业运营的自动化并提高效率。
在劳动力短缺和人力资源方面,如果企业想要提高效率,未来也将引入人工智能技术。

在考虑效率时,数据收集、积累和分析的时代已经过去。未来,基于数据的进一步优化(包括预测和自动化)的需求日益增长。

其中,使用 RFID 标签的系统被推荐为第一种方法,因为它的引入难度较低,而且广泛适用于各种情况。

什么是 RFID?


许多人认为很难理解 RFID 中的许多术语。

RFID 是 “Radio Frequency Identification “的缩写,直译为 “射频识别”,是一种利用无线电波以非接触方式接收数据的系统。

它是一种无线电波,有一定的带宽。
例如,用于电子支付的近距离无线通信(NFC)属于高频频段(短波:13.56 MHz),是一种短距离数据交换机制。
NFC 是 “Near Field Communication”(近距离无线通信)的缩写,因此它的意思是 “短距离无线通信”,而短距离就是这个意思。
NFC 可以理解为 RFID 的高频频段。

UHF 频段

另一个频段是 UHF 频段。
UHF 是 “超高频 “的缩写。日本的 920 MHz 频段和海外的 860 MHz 频段最为常见。

该波段主要用于工业应用。这是因为它在工业应用中的优势。

工业用 RFID 标签产品示例

  • 可固定在金属表面
  • 可嵌入混凝土中
  • 完全防水
  • 即使在快速移动的物体上也能读取。

上述高频带 NFC 规格只能在约 10 厘米的距离内读取。
想象一下电子支付。一般来说,距离就是这么远。

另一方面,UHF 波段可以在几米到 5 米之外读取。*这两种情况下的距离视条件而定。
此外,NFC(≒HF 频段)是一对一的通信,而 UHF 频段可以同时读取多个信息。

商业用途的 RFID


这可以成为在配送中心提取货物、在仓库管理库存和发货/收货的有效手段。


该系统可用于半成品的库存控制,以防止库存过多和成品短缺。

过去,我们从基于纸张的操作发展到条形码管理,然后是 QR 码管理。现在,同时扫描多个物品的 RFID(尤其是超高频)系统也很常见。

例如,广泛使用的 QR 码很容易受到灰尘和障碍物的影响,通常无法在预定地点使用。
在这方面,RFID 标签因其抗灰尘和障碍物的能力,可以适用于任何地点。

初始成本和运行成本都比以前低。
,如工业用 Raspberry Pi 等节省空间的系统,现在无需直接连接大型 PC 即可安装。
协同工作的物联网终端的处理速度在过去几年也有所提高。

这种环境也增加了 RFID 标签系统化使用的可能性。
 

UHF 标签读取系统(以 PL-R4 为例)

简而言之,RFID 和 NFC 包含非常详细和复杂的内容,因此您需要从一开始就选择专门用于工业用途的设备。

在工业用 Raspberry Pi 上,使用 NFC 或 UHF 频段系统化 RFID 阅读器并非不合理。它占用的空间很小,非常紧凑。
可连接一个小型监视器进行实时可视化。

Raspberry Pi 本身也是为工业应用而设计的,因此可以安装在恶劣的环境中。

在工业级 Raspberry Pi 上试用 NFC

从这里开始,我们将尝试 RFID 标签的实际操作。
建议实际运行系统,以便更好地了解加载速度等。

如果用 NFC(≈HF 频段)代替 UHF 频段,您就可以直接试用。

即使您需要测试示例程序或自己编写代码,现在也可以使用 ChatGPT 轻松编写程序。

我们还测试了在工业级 Raspberry Pi 上使用读取 NFC 标签的读写器的读写情况,该读写器是一种现成的商业产品。

本文件后半部分包含了一个可运行的 Python 程序,请复制并使用该程序。

当时的环境

Raspberry Pi 操作系统(靶心)安装在 PL-R4(使用 CM4)上,这是一款工业级 Raspberry Pi。32 位版本。

PL-R4 BASIC RJ Plus IP20 [样机]。
NFC 读卡器
  • PL-R4(配备 Raspberry Pi CM4)
  • 树莓派操作系统(靶心)32 位
  • 索尼 PaSoRi S380/P
  • 两种 NFC 读/写卡

索尼 S380/P 可通过 USB 电缆连接并使用 lsusb 检查来确认所需的 ID。

lsusb

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

安装 nfcpy


让我们先安装 nfcpy,并从一个示例程序中试用它。

我想用 pip 安装它,所以我将 Python 设为虚拟环境,然后安装了它。

source env/bin/activate然后就会在终端中看到 (env)。这说明你正处于虚拟环境中。

python3 -m venv env     # Create a Python virtual environment
source env/bin/activate # Activate the virtual environment
pip3 install nfcpy      # Install using pip3

最后,如果想从(env)回到原来的提示符,只需输入deactivate并运行即可。在这种情况下,我们将留在 (env) 中,直到一切结束。

安装 nfcpy 时,包括其他模块在内的版本如下(撰写本文时)

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

剩下的工作就是检查操作,然后执行程序进行读/写。

操作检查

首先,运行python3 -m nfc 。最终结果应如下所示

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

一开始可能会出错。

这是因为 NFC 阅读器是以用户权限运行的,尽管设备是以 root 权限运行的。
你可以用 sudo 运行它,但要根据终端信息添加 udev 规则。
命令已显示,复制并按原样执行即可。

下面的示例就是我所处的环境。
每个设备的 “供应商 “和 “产品 “值都不同。请勿原样复制。请在您的环境中按照终端显示的内容运行该命令

对于索尼 S380/P,054c06c3是 ID。

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 # Reload udev rules immediately


在此重启一次。

重启后,不再需要使用 sudo 权限运行
再次运行python3 -m nfc并检查是否存在开头所述的错误。

此后,处理 NFC 阅读器的 Python 程序也可以在没有 sudo 的情况下运行。

重启后,运行source env/bin/activate,然后运行示例程序。
如果您刚刚在主目录下运行了python3 -m venv env,则会出现source ~/env/bin/activate.
如果您没有 venv 环境,则会出现ModuleNotFoundError: No module named 'ndef'.

试用样本

通过 git clone 获取 nfcpy 示例文件。文件位于/nfcpy/examples

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

其中,我们将使用 tagtool.py 来检索信息。


添加 show 命令查看内容,添加 format 命令格式化(初始化)内容。

获取

python3 tagtool.py show

格式化(初始化)

python3 tagtool.py format

数据一旦写入 NFC,就可以锁定。如果没有上锁,内容可以被擦除并重新写入。据估计,数据可重写约 100,000 次。这就足够了。

在 tagtool.py 中,只有写入的命令有点复杂,所以我们稍后会要求你编写一个向 ChatGPT 写入的程序。

我们试用的第一张卡是纯白色的 NXP NTAG215。

下面是通过 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

标签类型为 Type2Tag,读/写容量为 492 字节,NDEF 信息仍为 0 字节,因此为空。

标签有多种类型,例如,Type3Tag 就是著名的电子货币 FeliCa。在这种情况下,它是 Type2Tag。

接下来,尝试另一张已写入数据的卡。

[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'


记录 1 是网站的 URL,记录 2 是以 UTF-8 编码的 Unicode(行业标准)字符串。

记录 2 中的第一个”\x02 “是一个控制字符(STX)。
以下字符为 UTF-8 字符,其中一个字节为一个英语字符(ASCII),三个字节为一个日语字符(Unicode)。

| 日语 | Unicode | UTF-8 编码 |
| :-:| :—–:| :———-:|
| ラ | U+30E9 | \xe3\x83\xa9

实际写入的是后面的字符串,不包括起始符号 = “\x02en”。

二进制数据转换

虽然不是特别必要,但我还是很好奇,所以我要求 ChatGPT 用 Python 创建代码,将以 UTF-8 编码的 Unicode 字符转换(解码)回文本。

去掉”\x02en “是为了便于阅读,因为在使用主笔时,”\x02en “是单独出现的。

decode_binary.py

# A script that receives a binary string (Unicode UTF-8) from standard input

def main():
    # Receive input (e.g., a string like \x02en\xe3\x83...)
    raw_input = input("Enter a binary string (e.g., \\x02en...):\n")

    try:
        # Convert the string to bytes (interpret escape sequences)
        byte_data = raw_input.encode('utf-8').decode('unicode_escape').encode('latin1')

        # Remove the first 3 bytes (\x02 + 'e' + 'n')
        if byte_data.startswith(b'\x02en'):
            byte_data = byte_data[3:]

        # Decode as UTF-8
        decoded_text = byte_data.decode('utf-8')

        print("\nDecoded result:")
        print(decoded_text)

    except Exception as e:
        print("\nAn error occurred:")
        print(e)

if __name__ == "__main__":
    main()

转换结果:
https://raspida.com
ラズパイダは非エンジニアでも楽しめるRaspberry Pi を紹介しています。

现在,您可以在转换时阅读它。

店铺


下面是我再次向 ChatGPT 索取的 Python 程序。


只能写入一条记录,不支持多条记录。

  • 通用资源识别号
  • 文本
  • vCard (名片)

URI 是一种写入 URL 的数据格式;当 URL 被写入 NFC 标签时,智能手机可以直接读取。相关的网络浏览器就会启动。

网站 NFC 标签。用 Safari 打开 “raspida.com”。

目前,其他格式的标签在您尝试用手机读取时不起作用。您需要一个单独的应用程序来读取 NFC 标签。

vCard 的数据格式应符合 vCard 3.0 规范。在本例中,由于 NFC 标签只有 137 个字节,没有足够的空间,因此没有设置 REV 等时间戳。

这是一种贴纸型 NFC 标签,就像这个一样: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("❌ This tag does not support 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":
        # Format vCard with empty name and include last and first names
        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 '{data_type}' is not supported.")
        return False

    tag.ndef.records = records
    print("✅ Write successful!")
    return True

def main():
    print("Select the data type:")
    print("  1: URI")
    print("  2: Text")
    print("  3: vCard")
    choice = input("Enter the number (1, 2, 3): ")

    language = "ja"  # Language is set to Japanese by default

    if choice == "1":
        data_type = "uri"
        content = input("Enter the URI: ")
        name = None
        last_name = first_name = None
    elif choice == "2":
        data_type = "text"
        content = input("Enter the text: ")
        name = None
        last_name = first_name = None
    elif choice == "3":
        data_type = "vcard"
        name = ""
        last_name = input("Last name (e.g., Yamada): ")
        first_name = input("First name (e.g., Taro): ")
        content = input("Phone number (e.g., 0312345678): ")
    else:
        print("Invalid selection. Exiting.")
        return

    print("Please touch the NFC card...")
    try:
        clf = nfc.ContactlessFrontend('usb')
        clf.connect(rdwr={'on-connect': lambda tag: write_tag(tag, data_type, content, language, name, last_name, first_name)})
    except Exception as e:
        print(f"❌ Could not connect to NFC reader: {e}")

if __name__ == "__main__":
    main()


写完文本后,检查每个文本是否由 tagtool.py 写入。

输入示例:1

Select the data type:
  1: URI
  2: Text
  3: vCard
Enter the number (1, 2, 3): 1
Enter the URI: http://raspida.com/
Please touch the NFC card...
✅ Write successful!

tagtool.py 显示的结果:

[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/'

输入示例:2

Select the data type:
  1: URI
  2: Text
  3: vCard
Enter the number (1, 2, 3): 2
Enter the text: This is a write test.
Please touch the NFC card...
✅ Write successful!

tagtool.py 显示的结果:

[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'

输入示例3

Select the data type:
  1: URI
  2: Text
  3: vCard
Enter the number (1, 2, 3): 3
Last name (e.g., Yamada): Yamada
First name (e.g., Taro): Taro
Phone number (e.g., 0312345678): 0312345678
Please touch the NFC card...
✅ Write successful!

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=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')

数据书写正确。

通过这种方式读写 NFC 标签可以用 Python 程序来实现。
,即使我这个非工程人员也能用 ChatGPT 编写一个无差错运行的程序。
在此代码的基础上,尝试修改为其他数据格式、多条记录等。


参考
https://nfcpy.readthedocs.io/en/latest/topics/get-started.html
https://vcardmaker.com


文章由拉斯必达提供

非工程师也能愉快使用的 Raspberry Pi 信息网站 raspida.com一个非工程师也能享受和使用的 Raspberry Pi 信息网站。他还为 PiLink 网站提供有关工业用 Raspberry Pi 的技术博客文章