mfrc522 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|