mfrc522 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/mfrc522.rb +613 -0
  3. metadata +64 -0
@@ -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
@@ -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: []