ESP32 MFRC522 RFID射频卡模块实验

射频识别RFID Radio Frequency IDentification 又称无线射频识别,是一种通讯技术,可通过无线电讯号识别特别目标并读写相关数据,而无需识别系统与特定目标之间建立机械或光学接触。、

恩智浦基于MFRC522 IC的RC522 RFID模块通常带有一个具有1KB内存的RFID卡标签和密匙卡标签。它可以编写标签,因此可以在其中存储某种秘密消息。

RC522 RFID 阅读器模块旨在创建13.56MHz的电磁场,用于与RFID标签ISO 14443A标准标签进行通信。阅读器可以通过4引脚串行外设接口SPI与微控制器进行通信,最大数据速率为10Mbps。它还支持通过I2C和UART协议进行通信。

该模块带有一个中断引脚。这很方便,因此不是一直问RFID模块‘是否有卡片在看?’,当标签进入附近时,模块会提醒我们。

该模块的工作电压为2.5V 至3.3V,逻辑引脚可以承受5V电压,可以连接到ESP32或任何5V逻辑微控制器,而无需使用任何逻辑电平转换器。

ESP32 Pico主板ESPBlockMFRC522 RFID射频卡模块
GP57/GP5SDA
GP184/GP18SCK
GP236/GP23MOSI
GP195/GP19MISO
**IRO
GNDGNDGND
3.3V3.3V3.3V

35.RFID_write.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----湖南创乐博智能科技有限公司----
#  文件名:35_RFID_write.py
#  版本:V2.0
#  author: zhulin
# 说明:RFID 写入数据
#####################################################

import mfrc522
from machine import Pin, SoftSPI
sck = Pin(18, Pin.OUT)
mosi = Pin(23, Pin.OUT)
miso = Pin(19, Pin.OUT)
spi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

sda = Pin(5, Pin.OUT)

def do_write():
    rdr = mfrc522.MFRC522(spi, sda)

    print("")
    print("Place card before reader to write address 0x08")
    print("")

    try:
        while True:
            #调用 rdr 对象的 request 方法,并将返回的结果赋值给 stat 和 tag_type 两个变量。

            (stat, tag_type) = rdr.request(rdr.REQIDL)

            if stat == rdr.OK:

                (stat, raw_uid) = rdr.anticoll()

                if stat == rdr.OK:
                    print("New card detected")
                    #tag_type是一个变量,表示卡片的类型,以16进制形式打印。
                    print("  - tag type: 0x%02x" % tag_type)
#                   #其中raw_uid是一个包含4个字节的数组,表示卡片的唯一标识符(UID),以16进制形式打印。
                    print("  - uid : 0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
                    print("")

                    if rdr.select_tag(raw_uid) == rdr.OK:

                        key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
                        #使用该密钥对读卡器进行认证操作,使用的认证方式是rdr.AUTHENT1A,密钥长度为8字节,认证的目标是raw_uid
                        if rdr.auth(rdr.AUTHENT1A, 8, key, raw_uid) == rdr.OK:
                            stat = rdr.write(8, b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f")
                            rdr.stop_crypto1()
                            if stat == rdr.OK:
                                print("Data written to card")
                            else:
                                print("Failed to write data to card")
                        else:
                            print("Authentication error")
                    else:
                        print("Failed to select tag")

    except KeyboardInterrupt:
        print("Bye")

# 程序入口
if __name__ == '__main__':
    do_write() # 写入卡 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]a

写入我们需要写入的数据如: 写入卡[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,15],把RFID标签放在读卡器上,写入成功。

问题: key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]

这是一个长度为6的密钥,由6个十六进制数表示。每个十六进制数的取值范围是0x00到0xFF,即0到255。该密钥的十六进制表示为[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]。

问题: MOSI

MOSI是一种常见的串行通信协议,它是SPI(Serial Peripheral Interface)总线的一部分。SPI是一种用于在微控制器和外部设备之间进行通信的协议。MOSI代表Master Out Slave In,意味着主设备向从设备发送数据。

在SPI总线中,有一个主设备和一个或多个从设备。主设备负责控制通信的时序和传输数据,而从设备则接收主设备发送的数据。MOSI线是主设备向从设备发送数据的线路。

MOSI线是SPI总线中的一个信号线,它用于将数据从主设备发送到从设备。主设备通过MOSI线将数据位发送给从设备,从设备通过MISO(Master In Slave Out)线将数据位发送回主设备。

问题: MISO

MISO是一种通信系统中的术语,代表着"Multiple Input, Single Output",即多输入单输出。在通信系统中,MISO表示有多个发送天线(输入),但只有一个接收天线(输出)。这种配置可以用于无线通信系统,如无线电、移动通信和无线传感器网络等。

MISO系统的优势之一是能够提高通信的可靠性和容量。通过利用多个发送天线,MISO系统可以实现空间多样性,减少信号传输过程中的干扰和衰落。这样可以提高信号的质量和覆盖范围,增加系统的容量和可靠性。

此外,MISO系统还可以通过使用多个发送天线来实现波束成形(beamforming)技术。波束成形可以将信号的能量聚焦在特定的方向上,从而提高信号的传输距离和质量。

总结一下,MISO是一种通信系统配置,它具有多个发送天线和一个接收天线。通过利用多个发送天线,MISO系统可以提高通信的可靠性、容量和覆盖范围。同时,它还可以应用波束成形技术来改善信号传输的距离和质量。

SoftSPI

SoftSPI是一种软件模拟的SPI(Serial Peripheral Interface)通信协议。SPI是一种常用的串行通信协议,用于在微控制器和外部设备之间进行数据传输。SoftSPI通过软件实现SPI的功能,而不需要硬件支持。

SoftSPI通常由以下几个要素组成:

  1. 主设备(Master):负责发起数据传输的设备。
  2. 从设备(Slave):接收主设备传输的数据的设备。
  3. 时钟线(SCK):用于同步主设备和从设备的时钟信号。
  4. 数据线(MOSI和MISO):用于在主设备和从设备之间传输数据。

SoftSPI的工作原理如下:

  1. 主设备通过控制时钟线和数据线向从设备发送指令或数据。
  2. 从设备根据时钟信号接收主设备发送的指令或数据,并通过数据线将响应发送回主设备。
  3. 主设备根据时钟信号接收从设备的响应。

SoftSPI相对于硬件SPI的优势在于它不依赖特定的硬件接口,可以在任何支持GPIO(通用输入输出)的平台上实现。但是由于软件实现的限制,SoftSPI的速度可能较慢,并且对处理器资源的消耗较大。

spi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

SPI(Serial Peripheral Interface)是一种串行外设接口,用于在微控制器和外部设备之间进行通信。在你提供的代码中,spi是一个SoftSPI对象,它是通过软件模拟实现的SPI接口。

该SoftSPI对象的初始化参数如下:

  • baudrate:指定通信速率,单位为Hz。
  • polarity:指定时钟极性,即时钟信号在空闲状态下的电平。0表示空闲状态下为低电平,1表示空闲状态下为高电平。
  • phase:指定时钟相位,即数据采样的时机。0表示在时钟的第一个边沿采样数据,1表示在时钟的第二个边沿采样数据。
  • sck:指定时钟线的引脚。
  • mosi:指定主设备输出从设备输入的引脚。
  • miso:指定主设备输入从设备输出的引脚。

通过SoftSPI对象,你可以使用相应的方法来进行SPI通信,例如发送数据、接收数据等。

rdr = mfrc522.MFRC522(spi, sda)

rdr = mfrc522.MFRC522(spi, sda) 是一段Python代码,它创建了一个MFRC522对象,并将其赋值给变量rdr。这段代码使用了mfrc522模块中的MFRC522类,该类用于与MFRC522 RFID读写器进行通信。

在这段代码中,spi和sda是参数,用于指定与MFRC522读写器通信所需的SPI总线和SDA引脚。SPI总线是一种串行通信协议,用于在微控制器和外部设备之间传输数据。SDA引脚是MFRC522读写器的数据线。

通过创建MFRC522对象,您可以使用该对象调用不同的方法来与MFRC522读写器进行交互,例如读取RFID卡片的数据、写入数据到RFID卡片等操作。

 print("  - uid : 0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
  • " - uid : 0x":这是一个字符串,表示打印UID的前缀部分。
  • %02x:这是一个格式化字符串,用于将整数以十六进制形式打印出来,并且保证至少两位宽度,不足两位时用0填充。
  • % (raw_uid, raw_uid, raw_uid, raw_uid):这是格式化字符串的参数部分,用于指定要插入到格式化字符串中的值。raw_uidraw_uidraw_uidraw_uid分别表示UID的四个字节的值。

所以,这段代码的作用是将UID的四个字节的值以十六进制形式打印出来,并在前面加上一个固定的前缀。

mfrc522.py

from machine import Pin, SPI
from os import uname


class MFRC522:

	OK = 0
	NOTAGERR = 1
	ERR = 2

	REQIDL = 0x26
	REQALL = 0x52
	AUTHENT1A = 0x60
	AUTHENT1B = 0x61

	def __init__(self, spi, cs):

		self.spi = spi
		self.cs = cs
		self.cs.value(1)
		self.spi.init()
		self.init()

	def _wreg(self, reg, val):

		self.cs.value(0)
		self.spi.write(b'%c' % int(0xff & ((reg << 1) & 0x7e)))
		self.spi.write(b'%c' % int(0xff & val))
		self.cs.value(1)

	def _rreg(self, reg):

		self.cs.value(0)
		self.spi.write(b'%c' % int(0xff & (((reg << 1) & 0x7e) | 0x80)))
		val = self.spi.read(1)
		self.cs.value(1)

		return val[0]

	def _sflags(self, reg, mask):
		self._wreg(reg, self._rreg(reg) | mask)

	def _cflags(self, reg, mask):
		self._wreg(reg, self._rreg(reg) & (~mask))

	def _tocard(self, cmd, send):

		recv = []
		bits = irq_en = wait_irq = n = 0
		stat = self.ERR

		if cmd == 0x0E:
			irq_en = 0x12
			wait_irq = 0x10
		elif cmd == 0x0C:
			irq_en = 0x77
			wait_irq = 0x30

		self._wreg(0x02, irq_en | 0x80)
		self._cflags(0x04, 0x80)
		self._sflags(0x0A, 0x80)
		self._wreg(0x01, 0x00)

		for c in send:
			self._wreg(0x09, c)
		self._wreg(0x01, cmd)

		if cmd == 0x0C:
			self._sflags(0x0D, 0x80)

		i = 2000
		while True:
			n = self._rreg(0x04)
			i -= 1
			if ~((i != 0) and ~(n & 0x01) and ~(n & wait_irq)):
				break

		self._cflags(0x0D, 0x80)

		if i:
			if (self._rreg(0x06) & 0x1B) == 0x00:
				stat = self.OK

				if n & irq_en & 0x01:
					stat = self.NOTAGERR
				elif cmd == 0x0C:
					n = self._rreg(0x0A)
					lbits = self._rreg(0x0C) & 0x07
					if lbits != 0:
						bits = (n - 1) * 8 + lbits
					else:
						bits = n * 8

					if n == 0:
						n = 1
					elif n > 16:
						n = 16

					for _ in range(n):
						recv.append(self._rreg(0x09))
			else:
				stat = self.ERR

		return stat, recv, bits

	def _crc(self, data):

		self._cflags(0x05, 0x04)
		self._sflags(0x0A, 0x80)

		for c in data:
			self._wreg(0x09, c)

		self._wreg(0x01, 0x03)

		i = 0xFF
		while True:
			n = self._rreg(0x05)
			i -= 1
			if not ((i != 0) and not (n & 0x04)):
				break

		return [self._rreg(0x22), self._rreg(0x21)]

	def init(self):

		self.reset()
		self._wreg(0x2A, 0x8D)
		self._wreg(0x2B, 0x3E)
		self._wreg(0x2D, 30)
		self._wreg(0x2C, 0)
		self._wreg(0x15, 0x40)
		self._wreg(0x11, 0x3D)
		self.antenna_on()

	def reset(self):
		self._wreg(0x01, 0x0F)

	def antenna_on(self, on=True):

		if on and ~(self._rreg(0x14) & 0x03):
			self._sflags(0x14, 0x03)
		else:
			self._cflags(0x14, 0x03)

	def request(self, mode):

		self._wreg(0x0D, 0x07)
		(stat, recv, bits) = self._tocard(0x0C, [mode])

		if (stat != self.OK) | (bits != 0x10):
			stat = self.ERR

		return stat, bits

	def anticoll(self):

		ser_chk = 0
		ser = [0x93, 0x20]

		self._wreg(0x0D, 0x00)
		(stat, recv, bits) = self._tocard(0x0C, ser)

		if stat == self.OK:
			if len(recv) == 5:
				for i in range(4):
					ser_chk = ser_chk ^ recv[i]
				if ser_chk != recv[4]:
					stat = self.ERR
			else:
				stat = self.ERR

		return stat, recv

	def select_tag(self, ser):

		buf = [0x93, 0x70] + ser[:5]
		buf += self._crc(buf)
		(stat, recv, bits) = self._tocard(0x0C, buf)
		return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR

	def auth(self, mode, addr, sect, ser):
		return self._tocard(0x0E, [mode, addr] + sect + ser[:4])[0]

	def stop_crypto1(self):
		self._cflags(0x08, 0x08)

	def read(self, addr):

		data = [0x30, addr]
		data += self._crc(data)
		(stat, recv, _) = self._tocard(0x0C, data)
		return recv if stat == self.OK else None

	def write(self, addr, data):

		buf = [0xA0, addr]
		buf += self._crc(buf)
		(stat, recv, bits) = self._tocard(0x0C, buf)

		if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
			stat = self.ERR
		else:
			buf = []
			for i in range(16):
				buf.append(data[i])
			buf += self._crc(buf)
			(stat, recv, bits) = self._tocard(0x0C, buf)
			if not (stat == self.OK) or not (bits == 4) or not ((recv[0] & 0x0F) == 0x0A):
				stat = self.ERR

		return stat

def select_tag(self, ser):

		buf = [0x93, 0x70] + ser[:5]
        #将buf与通过计算得到的校验码(使用_crc函数计算)拼接在一起。
		buf += self._crc(buf)
		(stat, recv, bits) = self._tocard(0x0C, buf)
		return self.OK if (stat == self.OK) and (bits == 0x18) else self.ERR

[0x93, 0x70]

[0x93, 0x70] 是一个十六进制数表示的列表,它包含两个元素:0x93 和 0x70。在计算机中,0x93 和 0x70 是十六进制数,分别对应十进制数的147 和 112。这个列表可以表示为 [147, 112]。

buf = [0x93, 0x70] + ser[:5]

这段代码的作用是将一个字节数组ser的前5个元素与[0x93, 0x70]拼接在一起,结果存储在变量buf中。
 

(stat, recv, bits) = self._tocard(0x0C, buf)

调用_tocard函数,将0x0C和拼接后的数据作为参数传入。该函数会将数据发送给设备,并返回一个元组(stat, recv, bits),其中stat表示状态,recv表示接收到的数据,bits表示位数。

0x0C

0x0C是一个十六进制数,表示的是十进制的12。在计算机中,十六进制是一种常用的表示数字的方式,它使用0-9和A-F来表示0-15这16个数。其中,A表示10,B表示11,C表示12,以此类推。

在计算机科学中,0x0C可以表示为二进制的00001100。它在编程中常用于表示字节、内存地址或者其他需要使用十六进制的场景。

0x有什么有什么作用?

在0x0C中,0x表示一个前缀,用于表示后面的数字是十六进制数。十六进制是一种常用的数值表示方式,它使用0-9和A-F来表示数字0-15。在计算机科学中,0x前缀通常用于表示内存地址或者十六进制的数值。

35.RFID_read.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----湖南创乐博智能科技有限公司----
#  文件名:35_RFID_read.py
#  版本:V2.0
#  author: zhulin
# 说明:RFID 读取数据
#####################################################
from time import sleep_ms
from machine import Pin, SoftSPI
from mfrc522 import MFRC522

sck = Pin(18, Pin.OUT)
mosi = Pin(23, Pin.OUT)
miso = Pin(19, Pin.OUT)
spi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)

sda = Pin(5, Pin.OUT)

def do_read():
    try:
        while True:
            rdr = MFRC522(spi, sda)
            uid = ""
            (stat, tag_type) = rdr.request(rdr.REQIDL)
            if stat == rdr.OK:
                (stat, raw_uid) = rdr.anticoll()
                if stat == rdr.OK:
                    print("New card detected")
                    print("  - tag type: 0x%02x" % tag_type)
                    uid = ("0x%02x%02x%02x%02x" % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3]))
                    print(uid)
                    if rdr.select_tag(raw_uid) == rdr.OK:
                        key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
                        
                        if rdr.auth(rdr.AUTHENT1A, 8, key, raw_uid) == rdr.OK:
                            print("Address 8 data: %s" % rdr.read(8))
                            rdr.stop_crypto1()
                        else:
                            print("Authentication error")
                    else:
                        print("Failed to select tag")                   
                        
                    sleep_ms(100)
    except KeyboardInterrupt:
        print("Bye")
        
# 程序入口
if __name__ == '__main__':
    do_read() # 读取成功,返回[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值