在物联网领域,我们经常听到 RFID 这个词,接下来讲解一下,什么是 RFID ?
RFID 全称为 Radio Frequency Identification,中文称“射频识别”技术。
现实中,我们经常用到这一技术的应用,比如门禁卡、考勤卡等。
关于 RFID,比较正式的定义是一种通信技术,可通过无线电讯号识别特定目标并读写相关数据,而无需识别系统与特定目标之间建立机械或光学接触。
我们看看它的原理图,如下图所示,RFID 由服务器、阅读器、标签三部分组成,其中阅读器与服务器之间由有线信道连接,通信过程通常认为是安全的,而阅读器与标签之间由无线信道连接,存在很大的安全隐患。
因项目涉及到电子标签的使用,对于此方面没有涉足的我一直担心于起通信协议的撰写,因为当今社会所有的产品都是向着高质量化方向发展。我所购买的是UHF-R200读写器,配置了30m超远距离探测的天线,成套的设备虽然提供了很多资料,但都是现成封装好的软件与命令协议,无法直接使用,而且指导书的字数多的可怕,而我们的项目本身所使用的又是Raspberry Pi 4B,网上关于R200与树莓派之间通信协议的资料根本没有,所以通过一下午的钻研,我终于攻克了此难点,特地来给大家一个台阶和参考。
1.UHF-R200 是一款超高频读写模块,体积小,功耗低,最大功率为28dbm,并且功率可调;主要用于典型读距离在 0-30m 以内的应用。该芯片具有低功耗,小尺寸、远距 离的特征,是低成本 RFID 系统的优良解决方案。UHF-R200模块有多种通信方式,有串口,SPI,USB通信。我们这里涉及到的是树莓派与R200模块采用的串口通信,PC端与R200采用的USB通信。
2.在R200固定发射功率(默认26dbm)情况下天线线圈决定R200读写RFID标签的最大范围,本文的线圈能识别读取30米内的RFID标签。
1.UHF-R200 2.天线
3.电子标签 4.PVC白卡(与电子标签一样)
3.纸质电子标签只有USR和EPC两个存储区,EPC用于存储12字节卡号,USR存储区可存最大64字节数据,一般利用用户存储区来放需要处理的数据。
4. 一般的电子标签有4个存储区:RFU,USR,EPC以及TID四个数据存储区,而PVC白卡只有EPC存储区,宽度为12个字节,一般存了卡号,就用不了别的地方了。
其实如果是简单运用的话,只需要记住EPC就OK了,因为这个是电子标签最核心的存储区,也是通信是收发的最明显的区域。
第一步:在PC端修改电子标签的EPC,改成自己需要的存储数据。




(注:这里修改EPC时,博主很久都没有成功,一直以为是哪个步骤头问题,实则不是,你所想写入的电子标签一定在读卡器范围里面,且只有它被读取,而且先在此界面把选择点上对勾,然后点单标签读取,之后再点现在的EPC,最后在写入。这里的几个点一定得多做几遍,因为写入不是一次就能成功的,需要鼠标一直点写入,说不定哪次就成功了。最主要的是,有可能不是一次就改成你所想改成的EPC,可能刚开始会改一部分,你只能把选择的对勾取消之后看看现在的EPC是多少了。)
好的,进行到这一步,电子标签已经按照你的想法初始化完成了。
第二步:在Raspberry Pi 4B上写串口命令,实现R200通讯协议。
树莓派接RFID模块进行写码这个需要看RFID模块引脚图以及引脚定义,比如:
串口需要接5个引脚,对应关系如下:
1).rfid的5v -> 树莓派的5v
2).rfid的gnd -> 树莓派的gnd
3).rfid的tx -> 树莓派的rx
4).rfid的rx -> 树莓派的tx
接线接好之后就可以在树莓派中写串口代码控制RFID了,这个需要去看对应的RFID模块的串行接口通讯协议文档。 比如:
命令帧(树莓派给R200)一共7个字
响应帧(R200给树莓派)一共8个字
上位机指令数据包格式如上,那么在树莓派中就可以用串口发命令了。对于代码段有不懂的可以私信我嗷!
import serial
import time
while True:
ser = serial.Serial('/dev/ttyAMA0',115200,timeout=0.5)
ser.isOpen()
ser.write(bytearray.fromhex('BB 00 27 00 03 22 FF FF 4A 7E')) #命令帧
ser.inWaiting()
version = ser.read(24)
ser.close()
xianshi =version[8] #16进制 1号鹅:8-18字 全是0001 0001 就是 17
print(xianshi) #16进制 2号鹅:8-18字 全是0010 0010 就是 34
print(version) #16进制 3号鹅:8-18字 全是0011 0011 就是 51
if xianshi==17:
print("1 goose") #这里用了三个电子标签作为3只鹅的识别
time.sleep(4)
if xianshi==34:
print("2 goose")
time.sleep(4)
if xianshi==51:
print("3 goose")
time.sleep(4)
time.sleep(0)
def uchar_checksum(data, byteorder='little'):
length = len(data)
checksum = 0
for i in range(0, length):
checksum += int.from_bytes(data[i:i+1], byteorder, signed=True)
checksum = ((~checksum) + 1 ) & 0xFF
return hex(checksum)
下面我放一些常用的命令帧
1. 单次读取卡号
Send:BB 00 22 00 00 22 7E
如果读到卡,模块回复:
BB 02 22 00 11 DC 30 00 00 00 00 00 00 00 00 00 00 00 00 00 0D AD FB 7E
BB 02 22 :是包识别符,长度3个字节;
00 11 :是包长度,16进制,0x11表示 17个字节,长度2个字节;
DC:
30 00:PC
00 00 00 00 00 00 00 00 00 00 00 00 00:卡号(EPC),可修改,占12个字节(通过修改EPC来改卡号)
0D AD:卡号的CRC
FB:Checksum;
7E;结束符;
如果读不到卡,或者无卡,模块回复:
BB 01 FF 00 01 15 16 7E
一共8个字节;
2. 群读卡号
Send:BB 00 27 00 03 22 FF FF 4A 7E
BB 00 27 :帧标志,3个字节;
00 03:数据长度,2个字节;0003表示3个字节;
22:保留字节;
FF FF : 读取次数,连续读取65535次;如果连续读取100次,填入00 64;
4A:Checksum,00 27 00 03 22 FF FF 每个字节都累加起来,得到0x024A;支取低8位 4A;
7E:结束符
Recv: BB 01 FF 00 01 15 16 7E
BB 01 FF 00 01 15 16 7E
.
.
.
BB 02 22 00 11 C8 34 00 E2 00 10 71 00 00 52 9B 09 40 B4 02 16 3D D3 7E
BB 01 FF 00 01 15 16 7E
BB 02 22 00 11 C9 34 00 E2 00 10 71 00 00 52 9B 09 40 B4 02 16 3D D4 7E
BB 02 22 00 11 C0 34 00 E2 00 10 71 00 00 52 9B 09 40 B4 02 16 3D CB 7E
BB 01 FF 00 01 15 16 7E
发出连读读取帧后,会联续的接收到收到两种类型的包。
BB 01 FF 00 01 15 16 7E
这是其中一种,表示读取失败;
BB 02 22 00 11 C0 34 00 E2 00 10 71 00 00 52 9B 09 40 B4 02 16 3D CB 7E
这是另外一种,表示读到卡号:
具体格式与单次读取卡号的回复包相同;
BB 02 22:帧标志,3个字节;
00 11:数据长度,11 表示16进制,实际为17个字节;
C0:信号强度;RSSI,一个字节;
34 00:PC,2个字节;
E2 00 10 71 00 00 52 9B 09 40 B4 02:卡号,12个字节;
16 3D :CRC2个字节;
CB:Checksum,02 ~ 16 3D 累加,取低8位;
7E:结束符;
3 .结束群读
Send: BB 00 28 00 00 28 7E
由于群读次数多时,操作时间会很长,客户可以发送该指令结束群读指令;
Recv: BB 01 28 00 01 00 2A 7E
模块执行结束群读指令的回复。
4. Read读卡内容
Send :BB 00 0C 00 07 23 00 00 00 00 60 00 96 7E (选择命令)
BB 00 39 00 09 00 00 00 00 03 00 00 00 04 49 7E (读命令)
其实是发送了两包数据,第一包是Select Set;第二包是写入包。Select Set的详细信息请参考上面Select Set的帧解析;下面介绍读取包:
BB 00 39 :是包识别符,长度3个字节;
00 09 :是包长度,16进制,0x09表示 9个字节,长度2个字节;
00 00 00 00:是访问密码(默认是00 00 00 00),长度4个字节;
03 : 表示选择用户存储区;
00 00 :表示读取的存储区的地址偏移量,00 00 指从0地址开始写入;
00 04 :表示去读的数据长度,00 04 表示写入4个字(8个字节);
49:Checksum,计算公式是,Checksum字节前面的所有字节,除了第一个字节BB外,每个字节的累加,结果只取低8位;
比如:00 39 00 09 00 00 00 00 03 00 00 00 04 累加的结果是:0x49,所以 Checksum就是0x49;
7E :结束字符;
Recv:BB 01 0C 00 01 00 0E 7E
BB 01 39 00 17 0E 34 00 E2 00 00 16 55 11 02 06 03 90 EA AF 01 02 03 04 05 06 07 08 49 7E
读取成功会接收到其实是2包,第一包是Select Set 的响应包;第二包是读取成功的响应包:
BB 01 39 :是包识别符,表示读取成功,长度3个字节;
00 17 :是包长度,16进制,0x17表示 23个字节,长度2个字节;
0E :PC+卡号的长度,16进制,0x0E表示 14个字节,长度1个字节;
34 00: 是PC值,这里不作解析,可以不处理;
E2 00 ~ EA AF :是成功写入的卡号,一共12个字节;
01 02 03 04 05 06 07 08 : 是读取的具体数据,一共8个字节。
49: Checksum;
7E:结束符;
读取失败会收到:
BB 01 0C 00 01 00 0E 7E
BB 01 FF 00 10 09 0E 34 00 E2 00 10 71 00 00 52 9B 09 40 B4 02 AA 7E
读取失败接收到其实是2包,第一包是Select Set 的响应包;第二包是读取失败的响应包:
BB 01 FF :是包识别符,表示出错,长度3个字节;
00 10 :是包长度,16进制,0x10表示 16个字节,长度2个字节;
09: 错误码: 0x09表示没找到卡;
0x16表示 访问密码错误;
0xA3表示超出读写范围;
5. Write写入内容
写入标签数据存储区的数据长度 DT 应不超过 32 个 word,即 64Byte 字节
Send:BB 00 0C 00 07 23 00 00 00 00 60 00 96 7E (选择命令)
BB 00 49 00 11 00 00 00 00 03 00 00 00 04 01 02 03 04 05 06 07 08 85 7E (写命令)
其实是发送了两包数据,第一包是Select Set;第二包是写入包。Select Set的详细信息请参考上面Select Set的帧解析;下面介绍写入包:
BB 00 49 :是包识别符,长度3个字节;
00 11 :是包长度,16进制,0x11表示 17个字节,长度2个字节;
00 00 00 00:是访问密码(默认是00 00 00 00),长度4个字节;
03 : 表示选择用户存储区;
00 00 :表示写入的存储区的地址偏移量,00 00 指从0地址开始写入;
00 04 :表示写入的数据长度,00 04 表示写入4个字(8个字节);
01 02 03 04 05 06 07 08 :是写入的数据;
85:Checksum,计算公式是,Checksum 字节前面的所有字节,除了第一个字节BB外,每个字节的累加,结果只取低8位;
比如:00 49 00 11 00 00 00 00 03 00 00 00 04 01 02 03 04 05 06 07 08 累加的结果是:0x85,所以 Checksum就是85;
7E :结束字符;
Recv:写入成功会收到:
BB 01 0C 00 01 00 0E 7E
BB 01 49 00 10 0E 34 00 E2 00 00 16 55 11 02 06 03 90 EA AF 00 2E 7E
写入成功会接收到其实是2包,第一包是Select Set 的响应包;第二包是写入的响应包:
BB 01 49 :是包识别符,表示写入成功,长度3个字节;
00 10 :是包长度,16进制,0x10表示 16个字节,长度2个字节;
0E :PC+卡号的长度,16进制,0x0E表示 14个字节,长度1个字节;
34 00: 是PC值,这里不作解析,可以不处理;
E2 00 ~ EA AF :是成功写入的卡号,一共12个字节;
00 : 表示操作成功;
2E : Checksum;
7E:结束符;
到这里,差不多就对RFID与树莓派的通信有了一定基础和认识,希望对于大家之后的学习有帮助!
还请大家记住我这个喜欢在深夜搞事情的快乐科研喵😋!