mfrc522 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/mfrc522.rb +613 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 76a0b3ccef69418c9bf365ca7ec159ad003c4fc2
|
|
4
|
+
data.tar.gz: 6024457f6053c4ae84dc28d839ee344bf0252365
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 76f98482e7e002bec39c51fc8b695e9dd25cbe16021c412397f743be6cd6b64747c5bdc29d993d63996194ad4828ccbd3f9736aae85768a38b7fc7916e52d110
|
|
7
|
+
data.tar.gz: a0fb447aa70005e6d4f1ae1d350783fe89663666c6ba5cdebaa053b00fe72dd30fe037df4af055d5689edabc83d041f9a77c220d778b39d435ab936ca8554623
|
data/lib/mfrc522.rb
ADDED
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
require 'pi_piper'
|
|
2
|
+
include PiPiper
|
|
3
|
+
|
|
4
|
+
class Mfrc522
|
|
5
|
+
|
|
6
|
+
# PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
|
|
7
|
+
PICC_REQA = 0x26 # REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame.
|
|
8
|
+
PICC_WUPA = 0x52 # Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame.
|
|
9
|
+
PICC_CT = 0x88 # Cascade Tag. Not really a command, but used during anti collision.
|
|
10
|
+
PICC_SEL_CL1 = 0x93 # Anti collision/Select, Cascade Level 1
|
|
11
|
+
PICC_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2
|
|
12
|
+
PICC_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3
|
|
13
|
+
PICC_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
|
|
14
|
+
# The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.pdf, Section 9)
|
|
15
|
+
# Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector.
|
|
16
|
+
# The read/write commands can also be used for MIFARE Ultralight.
|
|
17
|
+
PICC_MF_AUTH_KEY_A = 0x60 # Perform authentication with Key A
|
|
18
|
+
PICC_MF_AUTH_KEY_B = 0x61 # Perform authentication with Key B
|
|
19
|
+
PICC_MF_READ = 0x30 # Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight.
|
|
20
|
+
PICC_MF_WRITE = 0xA0 # Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight.
|
|
21
|
+
PICC_MF_DECREMENT = 0xC0 # Decrements the contents of a block and stores the result in the internal data register.
|
|
22
|
+
PICC_MF_INCREMENT = 0xC1 # Increments the contents of a block and stores the result in the internal data register.
|
|
23
|
+
PICC_MF_RESTORE = 0xC2 # Reads the contents of a block into the internal data register.
|
|
24
|
+
PICC_MF_TRANSFER = 0xB0 # Writes the contents of the internal data register to a block.
|
|
25
|
+
# The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
|
|
26
|
+
# The PICC_MF_READ and PICC_MF_WRITE can also be used for MIFARE Ultralight.
|
|
27
|
+
PICC_UL_WRITE = 0xA2 # Writes one 4 byte page to the PICC.
|
|
28
|
+
#
|
|
29
|
+
PICC_MF_ACK = 0xA # Mifare Acknowledge
|
|
30
|
+
|
|
31
|
+
# PCD commands
|
|
32
|
+
PCD_Idle = 0x00 # no action, cancels current command execution
|
|
33
|
+
PCD_Mem = 0x01 # stores 25 bytes into the internal buffer
|
|
34
|
+
PCD_GenRandomID = 0x02 # generates a 10-byte random ID number
|
|
35
|
+
PCD_CalcCRC = 0x03 # activates the CRC coprocessor or performs a self test
|
|
36
|
+
PCD_Transmit = 0x04 # transmits data from the FIFO buffer
|
|
37
|
+
PCD_NoCmdChange = 0x07 # no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit
|
|
38
|
+
PCD_Receive = 0x08 # activates the receiver circuits
|
|
39
|
+
PCD_Transceive = 0x0C # transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission
|
|
40
|
+
PCD_MFAuthent = 0x0E # performs the MIFARE standard authentication as a reader
|
|
41
|
+
PCD_SoftReset = 0x0F # resets the MFRC522
|
|
42
|
+
|
|
43
|
+
# PCD Command and Status Registers
|
|
44
|
+
CommandReg = 0x01 # starts and stops command execution
|
|
45
|
+
ComIEnReg = 0x02 # enable and disable interrupt request control bits
|
|
46
|
+
DivIEnReg = 0x03 # enable and disable interrupt request control bits
|
|
47
|
+
ComIrqReg = 0x04 # interrupt request bits
|
|
48
|
+
DivIrqReg = 0x05 # interrupt request bits
|
|
49
|
+
ErrorReg = 0x06 # error bits showing the error status of the last command executed
|
|
50
|
+
Status1Reg = 0x07 # communication status bits
|
|
51
|
+
Status2Reg = 0x08 # receiver and transmitter status bits
|
|
52
|
+
FIFODataReg = 0x09 # input and output of 64 byte FIFO buffer
|
|
53
|
+
FIFOLevelReg = 0x0A # number of bytes stored in the FIFO buffer
|
|
54
|
+
WaterLevelReg = 0x0B # level for FIFO underflow and overflow warning
|
|
55
|
+
ControlReg = 0x0C # miscellaneous control registers
|
|
56
|
+
BitFramingReg = 0x0D # adjustments for bit-oriented frames
|
|
57
|
+
CollReg = 0x0E # bit position of the first bit-collision detected on the RF interface
|
|
58
|
+
|
|
59
|
+
# PCD Command Registers
|
|
60
|
+
ModeReg = 0x11 # defines general modes for transmitting and receiving
|
|
61
|
+
TxModeReg = 0x12 # defines transmission data rate and framing
|
|
62
|
+
RxModeReg = 0x13 # defines reception data rate and framing
|
|
63
|
+
TxControlReg = 0x14 # controls the logical behavior of the antenna driver pins TX1 and TX2
|
|
64
|
+
TxASKReg = 0x15 # controls the setting of the transmission modulation
|
|
65
|
+
TxSelReg = 0x16 # selects the internal sources for the antenna driver
|
|
66
|
+
RxSelReg = 0x17 # selects internal receiver settings
|
|
67
|
+
RxThresholdReg = 0x18 # selects thresholds for the bit decoder
|
|
68
|
+
DemodReg = 0x19 # defines demodulator settings
|
|
69
|
+
MfTxReg = 0x1C # controls some MIFARE communication transmit parameters
|
|
70
|
+
MfRxReg = 0x1D # controls some MIFARE communication receive parameters
|
|
71
|
+
SerialSpeedReg = 0x1F # selects the speed of the serial UART interface
|
|
72
|
+
|
|
73
|
+
# PCD Configuration Registers
|
|
74
|
+
CRCResultRegH = 0x21 # shows the MSB and LSB values of the CRC calculation
|
|
75
|
+
CRCResultRegL = 0x22
|
|
76
|
+
ModWidthReg = 0x24 # controls the ModWidth setting?
|
|
77
|
+
RFCfgReg = 0x26 # configures the receiver gain
|
|
78
|
+
GsNReg = 0x27 # selects the conductance of the antenna driver pins TX1 and TX2 for modulation
|
|
79
|
+
CWGsPReg = 0x28 # defines the conductance of the p-driver output during periods of no modulation
|
|
80
|
+
ModGsPReg = 0x29 # defines the conductance of the p-driver output during periods of modulation
|
|
81
|
+
TModeReg = 0x2A # defines settings for the internal timer
|
|
82
|
+
TPrescalerReg = 0x2B # the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
|
|
83
|
+
TReloadRegH = 0x2C # defines the 16-bit timer reload value
|
|
84
|
+
TReloadRegL = 0x2D
|
|
85
|
+
TCounterValueRegH = 0x2E # shows the 16-bit timer value
|
|
86
|
+
TCounterValueRegL = 0x2F
|
|
87
|
+
|
|
88
|
+
# PCD Test Registers
|
|
89
|
+
TestSel1Reg = 0x31 # general test signal configuration
|
|
90
|
+
TestSel2Reg = 0x32 # general test signal configuration
|
|
91
|
+
TestPinEnReg = 0x33 # enables pin output driver on pins D1 to D7
|
|
92
|
+
TestPinValueReg = 0x34 # defines the values for D1 to D7 when it is used as an I/O bus
|
|
93
|
+
TestBusReg = 0x35 # shows the status of the internal test bus
|
|
94
|
+
AutoTestReg = 0x36 # controls the digital self test
|
|
95
|
+
VersionReg = 0x37 # shows the software version
|
|
96
|
+
AnalogTestReg = 0x38 # controls the pins AUX1 and AUX2
|
|
97
|
+
TestDAC1Reg = 0x39 # defines the test value for TestDAC1
|
|
98
|
+
TestDAC2Reg = 0x3A # defines the test value for TestDAC2
|
|
99
|
+
TestADCReg = 0x3B # shows the value of ADC I and Q channels
|
|
100
|
+
|
|
101
|
+
def initialize(nrstpd = 24, chip = 0, spd = 8000000, timer = 50)
|
|
102
|
+
chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0,
|
|
103
|
+
1 => PiPiper::Spi::CHIP_SELECT_1,
|
|
104
|
+
2 => PiPiper::Spi::CHIP_SELECT_BOTH,
|
|
105
|
+
3 => PiPiper::Spi::CHIP_SELECT_NONE }
|
|
106
|
+
@spi_chip = chip_option[chip]
|
|
107
|
+
@spi_spd = spd
|
|
108
|
+
|
|
109
|
+
# Power it up
|
|
110
|
+
nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out)
|
|
111
|
+
nrstpd_pin.on
|
|
112
|
+
sleep 1.0 / 20.0 # Wait 50ms
|
|
113
|
+
|
|
114
|
+
soft_reset # Perform software reset
|
|
115
|
+
|
|
116
|
+
write_spi(TModeReg, 0x8D) # Start timer by setting TAuto=1, and higher part of TPrescalerReg
|
|
117
|
+
write_spi(TPrescalerReg, 0x3E) # Set lower part of TPrescalerReg, and results in 2khz timer (f_timer = 13.56 MHz / (2*TPreScaler+1))
|
|
118
|
+
write_spi(TReloadRegH, (timer >> 8))
|
|
119
|
+
write_spi(TReloadRegL, (timer & 0xFF)) # 50 ticks @2khz defines 25ms per timer cycle
|
|
120
|
+
|
|
121
|
+
write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
|
|
122
|
+
write_spi(ModeReg, 0x3D) # Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
|
|
123
|
+
|
|
124
|
+
antenna_on # Turn antenna on. They were disabled by the reset.
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def soft_reset
|
|
128
|
+
write_spi(CommandReg, PCD_SoftReset)
|
|
129
|
+
sleep 1.0 / 20.0 # wait 50ms
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def read_spi(reg)
|
|
133
|
+
output = 0
|
|
134
|
+
PiPiper::Spi.begin do |spi|
|
|
135
|
+
spi.chip_select_active_low(true)
|
|
136
|
+
spi.bit_order Spi::MSBFIRST
|
|
137
|
+
spi.clock @spi_spd
|
|
138
|
+
|
|
139
|
+
spi.chip_select(@spi_chip) do
|
|
140
|
+
spi.write((reg << 1) & 0x7E | 0x80)
|
|
141
|
+
output = spi.read
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
output
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def write_spi(reg, values)
|
|
148
|
+
PiPiper::Spi.begin do |spi|
|
|
149
|
+
spi.chip_select_active_low(true)
|
|
150
|
+
spi.bit_order Spi::MSBFIRST
|
|
151
|
+
spi.clock @spi_spd
|
|
152
|
+
|
|
153
|
+
spi.chip_select(@spi_chip) do
|
|
154
|
+
spi.write((reg << 1) & 0x7E, *values)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def write_spi_set_bitmask(reg, mask)
|
|
160
|
+
value = read_spi(reg)
|
|
161
|
+
write_spi(reg, value | mask)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def write_spi_clear_bitmask(reg, mask)
|
|
165
|
+
value = read_spi(reg)
|
|
166
|
+
write_spi(reg, value & (~mask))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def antenna_on
|
|
170
|
+
value = read_spi(TxControlReg)
|
|
171
|
+
write_spi_set_bitmask(TxControlReg, 0x03) if (value & 0x03) != 0x03
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def antenna_off
|
|
175
|
+
write_spi_clear_bitmask(TxControlReg, 0x03)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB
|
|
179
|
+
def antenna_gain(level = nil)
|
|
180
|
+
unless level.nil?
|
|
181
|
+
level = 1 if level > 6 || level < 1
|
|
182
|
+
write_spi_set_bitmask(RFCfgReg, ((level + 1) << 4))
|
|
183
|
+
end
|
|
184
|
+
(read_spi(RFCfgReg) & 0x70) >> 4
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def calculate_crc(data)
|
|
188
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
|
189
|
+
write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
|
|
190
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
|
191
|
+
write_spi(FIFODataReg, data) # Write data to the FIFO
|
|
192
|
+
write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
|
|
193
|
+
|
|
194
|
+
# Wait for the command to complete
|
|
195
|
+
i = 5000
|
|
196
|
+
loop do
|
|
197
|
+
irq = read_spi(DivIrqReg)
|
|
198
|
+
break if (irq & 0x04) != 0
|
|
199
|
+
return :status_pcd_timeout if i == 0
|
|
200
|
+
i -= 1
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
|
|
204
|
+
|
|
205
|
+
result = []
|
|
206
|
+
result << read_spi(CRCResultRegL)
|
|
207
|
+
result << read_spi(CRCResultRegH)
|
|
208
|
+
|
|
209
|
+
return :status_ok, result
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def append_crc(data)
|
|
213
|
+
status, crc = calculate_crc(data)
|
|
214
|
+
return status if status != :status_ok
|
|
215
|
+
data << crc[0] << crc[1]
|
|
216
|
+
|
|
217
|
+
return :status_ok, data
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def check_crc(data)
|
|
221
|
+
status, crc = calculate_crc(data[0..-3])
|
|
222
|
+
return status if status != :status_ok
|
|
223
|
+
return :status_crc_error if data[-2] != crc[0] || data[-1] != crc[1]
|
|
224
|
+
|
|
225
|
+
return :status_ok
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def communicate_with_picc(command, send_data, framing_bit = 0, check_crc = false)
|
|
229
|
+
wait_irq = 0x00
|
|
230
|
+
wait_irq = 0x10 if command == PCD_MFAuthent
|
|
231
|
+
wait_irq = 0x30 if command == PCD_Transceive
|
|
232
|
+
|
|
233
|
+
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
|
234
|
+
write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits
|
|
235
|
+
write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
|
|
236
|
+
write_spi(FIFODataReg, send_data) # Write sendData to the FIFO
|
|
237
|
+
write_spi(BitFramingReg, framing_bit) # Bit adjustments
|
|
238
|
+
write_spi(CommandReg, command) # Execute the command
|
|
239
|
+
if command == PCD_Transceive
|
|
240
|
+
write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Wait for the command to complete
|
|
244
|
+
i = 2000
|
|
245
|
+
loop do
|
|
246
|
+
irq = read_spi(ComIrqReg)
|
|
247
|
+
break if (irq & wait_irq) != 0
|
|
248
|
+
return :status_picc_timeout if (irq & 0x01) != 0
|
|
249
|
+
return :status_pcd_timeout if i == 0
|
|
250
|
+
i -= 1
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Check for error
|
|
254
|
+
error = read_spi(ErrorReg)
|
|
255
|
+
return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
|
|
256
|
+
|
|
257
|
+
# Receiving data
|
|
258
|
+
received_data = []
|
|
259
|
+
data_length = read_spi(FIFOLevelReg)
|
|
260
|
+
while data_length > 0 do
|
|
261
|
+
data = read_spi(FIFODataReg)
|
|
262
|
+
received_data << data
|
|
263
|
+
data_length -=1
|
|
264
|
+
end
|
|
265
|
+
valid_bits = read_spi(ControlReg) & 0x07
|
|
266
|
+
|
|
267
|
+
# Check CRC if requested
|
|
268
|
+
if !received_data.empty? && check_crc
|
|
269
|
+
return :status_mifare_nack if received_data.count == 1 && valid_bits == 4
|
|
270
|
+
return :status_crc_error if received_data.count < 2 || valid_bits != 0
|
|
271
|
+
|
|
272
|
+
status = check_crc(received_data)
|
|
273
|
+
return status if status != :status_ok
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
status = :status_ok
|
|
277
|
+
status = :status_collision if (error & 0x08) != 0 # CollErr
|
|
278
|
+
|
|
279
|
+
return status, received_data, valid_bits
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Wakes PICC from HALT or IDLE to ACTIVE state
|
|
283
|
+
#
|
|
284
|
+
# Accept PICC_REQA and PICC_WUPA command
|
|
285
|
+
def picc_request(picc_command)
|
|
286
|
+
write_spi_clear_bitmask(CollReg, 0x80) # ValuesAfterColl=1 => Bits received after collision are cleared.
|
|
287
|
+
|
|
288
|
+
status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07)
|
|
289
|
+
|
|
290
|
+
return status if status != :status_ok
|
|
291
|
+
return :status_error if valid_bits != 0 # REQA or WUPA command return 16 bits(full byte)
|
|
292
|
+
|
|
293
|
+
return :status_ok
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Instruct PICC in ACTIVE state go to HALT
|
|
297
|
+
def picc_halt
|
|
298
|
+
buffer = [PICC_HLTA, 0]
|
|
299
|
+
|
|
300
|
+
# Calculate CRC and append it into buffer
|
|
301
|
+
status, buffer = append_crc(buffer)
|
|
302
|
+
return status if status != :status_ok
|
|
303
|
+
|
|
304
|
+
status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer)
|
|
305
|
+
|
|
306
|
+
# PICC in HALT state will not respond
|
|
307
|
+
# If PICC sent reply, means it didn't acknowledge the command we sent
|
|
308
|
+
return :status_ok if status == :status_picc_timeout
|
|
309
|
+
return :status_error if status == :status_ok
|
|
310
|
+
|
|
311
|
+
return status
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# PICC must be in state ACTIVE
|
|
315
|
+
def picc_select
|
|
316
|
+
# Description of buffer structure:
|
|
317
|
+
#
|
|
318
|
+
# Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
|
|
319
|
+
# Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits.
|
|
320
|
+
# Byte 2: UID-data or Cascade Tag
|
|
321
|
+
# Byte 3: UID-data
|
|
322
|
+
# Byte 4: UID-data
|
|
323
|
+
# Byte 5: UID-data
|
|
324
|
+
# Byte 6: Block Check Character - XOR of bytes 2-5
|
|
325
|
+
# Byte 7: CRC_A
|
|
326
|
+
# Byte 8: CRC_A
|
|
327
|
+
# The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level.
|
|
328
|
+
#
|
|
329
|
+
# Description of bytes 2-5
|
|
330
|
+
#
|
|
331
|
+
# UID size Cascade level Byte2 Byte3 Byte4 Byte5
|
|
332
|
+
# ======== ============= ===== ===== ===== =====
|
|
333
|
+
# 4 bytes 1 uid0 uid1 uid2 uid3
|
|
334
|
+
# 7 bytes 1 CT uid0 uid1 uid2
|
|
335
|
+
# 2 uid3 uid4 uid5 uid6
|
|
336
|
+
# 10 bytes 1 CT uid0 uid1 uid2
|
|
337
|
+
# 2 CT uid3 uid4 uid5
|
|
338
|
+
# 3 uid6 uid7 uid8 uid9
|
|
339
|
+
|
|
340
|
+
write_spi_clear_bitmask(CollReg, 0x80) # ValuesAfterColl=1 => Bits received after collision are cleared.
|
|
341
|
+
select_level = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3]
|
|
342
|
+
uid = []
|
|
343
|
+
|
|
344
|
+
for current_cascade_level in 0..2
|
|
345
|
+
buffer = [select_level[current_cascade_level]]
|
|
346
|
+
current_level_known_bits = 0
|
|
347
|
+
|
|
348
|
+
loop do
|
|
349
|
+
if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything
|
|
350
|
+
tx_last_bits = 0
|
|
351
|
+
buffer[1] = 0x70 # NVB - We're sending full length byte[0..6]
|
|
352
|
+
buffer << (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
|
|
353
|
+
|
|
354
|
+
# Append CRC to buffer
|
|
355
|
+
status, buffer = append_crc(buffer)
|
|
356
|
+
return status if status != :status_ok
|
|
357
|
+
else
|
|
358
|
+
tx_last_bits = current_level_known_bits % 8
|
|
359
|
+
uid_full_byte = current_level_known_bits / 8
|
|
360
|
+
all_full_byte = 2 + uid_full_byte # length of SEL + NVB + UID
|
|
361
|
+
buffer[1] = (all_full_byte << 4) + tx_last_bits # NVB
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
framing_bit = (tx_last_bits << 4) + tx_last_bits
|
|
365
|
+
|
|
366
|
+
# Try to fetch UID
|
|
367
|
+
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, buffer, framing_bit)
|
|
368
|
+
return status if status != :status_ok
|
|
369
|
+
|
|
370
|
+
# Append received UID into buffer if not doing full select
|
|
371
|
+
buffer = buffer[0...all_full_byte] + received_data if current_level_known_bits < 32
|
|
372
|
+
|
|
373
|
+
# Handle collision
|
|
374
|
+
if status == :status_collision
|
|
375
|
+
collision = read_spi(CollReg)
|
|
376
|
+
|
|
377
|
+
return :status_collision if (collision & 0x20) != 0 # CollPosNotValid - We don't know where collision happened
|
|
378
|
+
collision_position = collision & 0x1F
|
|
379
|
+
collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32
|
|
380
|
+
return :status_internal_error if collision_position <= current_level_known_bits
|
|
381
|
+
|
|
382
|
+
# Mark the bit
|
|
383
|
+
current_level_known_bits = collision_position
|
|
384
|
+
uid_bit = (current_level_known_bits - 1) % 8
|
|
385
|
+
uid_byte = (current_level_known_bits / 8) + (uid_bit != 0 ? 1 : 0)
|
|
386
|
+
buffer[1 + uid_byte] |= (1 << uid_bit)
|
|
387
|
+
elsif status == :status_ok
|
|
388
|
+
break if current_level_known_bits >= 32
|
|
389
|
+
current_level_known_bits = 32 # We've already known all bits, loop again for a complete select
|
|
390
|
+
else
|
|
391
|
+
return status
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# We've finished current cascade level
|
|
396
|
+
# Check and collect all uid in this level
|
|
397
|
+
|
|
398
|
+
# Append UID
|
|
399
|
+
uid << buffer[2] if buffer[2] != PICC_CT
|
|
400
|
+
uid << buffer[3] << buffer[4] << buffer[5]
|
|
401
|
+
|
|
402
|
+
# Check the result of full select
|
|
403
|
+
return :status_sak_error if received_data.count != 3 || valid_bits != 0 # Select Acknowledge is 1 byte + CRC_A
|
|
404
|
+
|
|
405
|
+
status = check_crc(received_data)
|
|
406
|
+
return status if status != :status_ok
|
|
407
|
+
|
|
408
|
+
sak = received_data[0]
|
|
409
|
+
|
|
410
|
+
break if (received_data[0] & 0x04) == 0 # No more cascade level
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
return :status_ok, uid, sak
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def picc_type(sak)
|
|
417
|
+
sak &= 0x7F
|
|
418
|
+
|
|
419
|
+
case sak
|
|
420
|
+
when 0x04
|
|
421
|
+
'PICC_TYPE_NOT_COMPLETE'
|
|
422
|
+
when 0x09
|
|
423
|
+
'PICC_TYPE_MIFARE_MINI'
|
|
424
|
+
when 0x08
|
|
425
|
+
'PICC_TYPE_MIFARE_1K'
|
|
426
|
+
when 0x18
|
|
427
|
+
'PICC_TYPE_MIFARE_4K'
|
|
428
|
+
when 0x00
|
|
429
|
+
'PICC_TYPE_MIFARE_UL'
|
|
430
|
+
when 0x10, 0x11
|
|
431
|
+
'PICC_TYPE_MIFARE_PLUS'
|
|
432
|
+
when 0x01
|
|
433
|
+
'PICC_TYPE_TNP3XXX'
|
|
434
|
+
when 0x20
|
|
435
|
+
'PICC_TYPE_ISO_14443_4'
|
|
436
|
+
when 0x40
|
|
437
|
+
'PICC_TYPE_ISO_18092'
|
|
438
|
+
else
|
|
439
|
+
'PICC_TYPE_UNKNOWN'
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
#
|
|
444
|
+
# PICC must be selected before calling for authentication
|
|
445
|
+
# Remember to deauthenticate after communication, or no new communication can be made
|
|
446
|
+
#
|
|
447
|
+
# Accept PICC_MF_AUTH_KEY_A or PICC_MF_AUTH_KEY_B command
|
|
448
|
+
# Checks datasheets for block address numbering of your PICC
|
|
449
|
+
#
|
|
450
|
+
def mifare_authenticate(command, block_addr, sector_key, uid)
|
|
451
|
+
#
|
|
452
|
+
# Buffer[12]: {command, block_addr, sector_key[6], uid[4]}
|
|
453
|
+
#
|
|
454
|
+
buffer = [command, block_addr]
|
|
455
|
+
buffer += sector_key[0..5]
|
|
456
|
+
buffer += uid[0..3]
|
|
457
|
+
|
|
458
|
+
status, _received_data, _valid_bits = communicate_with_picc(PCD_MFAuthent, buffer)
|
|
459
|
+
|
|
460
|
+
return status if status != :status_ok
|
|
461
|
+
return :status_auth_failed if (read_spi(Status2Reg) & 0x08) == 0
|
|
462
|
+
|
|
463
|
+
return :status_ok
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def mifare_deauthenticate
|
|
467
|
+
write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Helper that append crc to buffer and check mifare acknowledge
|
|
471
|
+
def mifare_transceive(send_data, accept_timeout = false)
|
|
472
|
+
# Append CRC
|
|
473
|
+
status, send_data = append_crc(send_data)
|
|
474
|
+
return status if status != :status_ok
|
|
475
|
+
|
|
476
|
+
# Transfer data
|
|
477
|
+
status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
|
|
478
|
+
return :status_ok if status == :status_picc_timeout && accept_timeout
|
|
479
|
+
return status if status != :status_ok
|
|
480
|
+
|
|
481
|
+
# Check mifare acknowledge
|
|
482
|
+
return :status_error if received_data.count != 1 || valid_bits != 4 # ACK is 4 bits long
|
|
483
|
+
return :status_mifare_nack if received_data[0] != PICC_MF_ACK
|
|
484
|
+
|
|
485
|
+
return :status_ok
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def mifare_read(block_addr)
|
|
489
|
+
buffer = [PICC_MF_READ, block_addr]
|
|
490
|
+
|
|
491
|
+
status, buffer = append_crc(buffer)
|
|
492
|
+
return status if status != :status_ok
|
|
493
|
+
|
|
494
|
+
status, received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer, 0, true)
|
|
495
|
+
return status if status != :status_ok
|
|
496
|
+
|
|
497
|
+
return :status_ok, received_data
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def mifare_write(block_addr, send_data)
|
|
501
|
+
buffer = [PICC_MF_WRITE, block_addr]
|
|
502
|
+
|
|
503
|
+
# Ask PICC if we can write to block_addr
|
|
504
|
+
status = mifare_transceive(buffer)
|
|
505
|
+
return status if status != :status_ok
|
|
506
|
+
|
|
507
|
+
# Then start transfer our data
|
|
508
|
+
status = mifare_transceive(send_data)
|
|
509
|
+
return status if status != :status_ok
|
|
510
|
+
|
|
511
|
+
return :status_ok
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def mifare_ultralight_write(page, send_data)
|
|
515
|
+
# Page 2-15, each 4 bytes
|
|
516
|
+
buffer = [PICC_UL_WRITE, page]
|
|
517
|
+
buffer += send_data[0..3]
|
|
518
|
+
|
|
519
|
+
status = mifare_transceive(buffer)
|
|
520
|
+
return status if status != :status_ok
|
|
521
|
+
|
|
522
|
+
return :status_ok
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
# Helper for reading value block
|
|
526
|
+
def mifare_get_value(block_addr)
|
|
527
|
+
status, received_data = mifare_read(block_addr)
|
|
528
|
+
return status if status != :status_ok
|
|
529
|
+
|
|
530
|
+
value = (received_data[3] << 24) + (received_data[2] << 16) + (received_data[1] << 8) + received_data[0]
|
|
531
|
+
|
|
532
|
+
return :status_ok, value
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Helper for writing value block
|
|
536
|
+
def mifare_set_value(block_addr, value)
|
|
537
|
+
# Value block format
|
|
538
|
+
#
|
|
539
|
+
# byte 0..3: 32 bit value in little endian
|
|
540
|
+
# byte 4..7: copy of byte 0..3, with inverted bits (aka. XOR 255)
|
|
541
|
+
# byte 8..11: copy of byte 0..3
|
|
542
|
+
# byte 12: index of backup block (can be any value)
|
|
543
|
+
# byte 13: copy of byte 12 with inverted bits (aka. XOR 255)
|
|
544
|
+
# byte 14: copy of byte 12
|
|
545
|
+
# byte 15: copy of byte 13
|
|
546
|
+
|
|
547
|
+
buffer[0] = value & 0xFF
|
|
548
|
+
buffer[1] = (value >> 8) & 0xFF
|
|
549
|
+
buffer[2] = (value >> 16) & 0xFF
|
|
550
|
+
buffer[3] = (value >> 24) & 0xFF
|
|
551
|
+
buffer[4] = ~buffer[0]
|
|
552
|
+
buffer[5] = ~buffer[1]
|
|
553
|
+
buffer[6] = ~buffer[2]
|
|
554
|
+
buffer[7] = ~buffer[3]
|
|
555
|
+
buffer[8] = buffer[0]
|
|
556
|
+
buffer[9] = buffer[1]
|
|
557
|
+
buffer[10] = buffer[2]
|
|
558
|
+
buffer[11] = buffer[3]
|
|
559
|
+
buffer[12] = block_addr
|
|
560
|
+
buffer[13] = ~block_addr
|
|
561
|
+
buffer[14] = buffer[12]
|
|
562
|
+
buffer[15] = buffer[13]
|
|
563
|
+
|
|
564
|
+
mifare_write(blockAddr, buffer)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# Helper for increment, decrement, and restore command
|
|
568
|
+
def mifare_two_step(command, block_addr, value)
|
|
569
|
+
buffer = [command, block_addr]
|
|
570
|
+
send_data = [ # Split integer into array of bytes
|
|
571
|
+
value & 0xFF,
|
|
572
|
+
(value >> 8) & 0xFF,
|
|
573
|
+
(value >> 16) & 0xFF,
|
|
574
|
+
(value >> 24) & 0xFF
|
|
575
|
+
]
|
|
576
|
+
|
|
577
|
+
# Ask PICC if we can write to block_addr
|
|
578
|
+
status = mifare_transceive(buffer)
|
|
579
|
+
return status if status != :status_ok
|
|
580
|
+
|
|
581
|
+
# Then start transfer our data
|
|
582
|
+
status = mifare_transceive(send_data, true) # Accept timeout
|
|
583
|
+
return status if status != :status_ok
|
|
584
|
+
|
|
585
|
+
return :status_ok
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# MIFARE Classic only
|
|
589
|
+
def mifare_increment(block_addr, delta)
|
|
590
|
+
mifare_two_step(PICC_MF_INCREMENT, block_addr, delta)
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
# MIFARE Classic only
|
|
594
|
+
def mifare_decrement(block_addr, delta)
|
|
595
|
+
mifare_two_step(PICC_MF_DECREMENT, block_addr, delta)
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
# MIFARE Classic only
|
|
599
|
+
def mifare_restore(block_addr)
|
|
600
|
+
mifare_two_step(PICC_MF_RESTORE, block_addr, 0)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
# MIFARE Classic only
|
|
604
|
+
def mifare_transfer(block_addr)
|
|
605
|
+
buffer = [PICC_MF_TRANSFER, block_addr]
|
|
606
|
+
|
|
607
|
+
status = mifare_transceive(buffer)
|
|
608
|
+
return status if status != :status_ok
|
|
609
|
+
|
|
610
|
+
return :status_ok
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mfrc522
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- atitan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2016-03-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: pi_piper
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 2.0.0
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '2.0'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.0.0
|
|
33
|
+
description:
|
|
34
|
+
email: commit@atifans.net
|
|
35
|
+
executables: []
|
|
36
|
+
extensions: []
|
|
37
|
+
extra_rdoc_files: []
|
|
38
|
+
files:
|
|
39
|
+
- lib/mfrc522.rb
|
|
40
|
+
homepage: https://github.com/atitan/MFRC522_Ruby
|
|
41
|
+
licenses:
|
|
42
|
+
- MIT
|
|
43
|
+
metadata: {}
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options: []
|
|
46
|
+
require_paths:
|
|
47
|
+
- lib
|
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
requirements: []
|
|
59
|
+
rubyforge_project:
|
|
60
|
+
rubygems_version: 2.4.6
|
|
61
|
+
signing_key:
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: MFRC522 RFID Reader Library for RaspberryPi
|
|
64
|
+
test_files: []
|