mfrc522 1.0.2 → 3.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 215975fcbc3557e0178dfb4914d1f7c485da80ec
4
- data.tar.gz: 23d6d8c256dcd032ac97a4c8cfc24484d2b63f1f
2
+ SHA256:
3
+ metadata.gz: b180534b3c1fff35f759792d021ba7005b460838e82b43782341f358d314ae5c
4
+ data.tar.gz: 77f1e85d84082d57ed27dbba18aea6b6e1e9949327f62ed135e2ec1389e12efe
5
5
  SHA512:
6
- metadata.gz: 14924647fdbe471062ef2cf950148f8aef115c3a0a39e77d599a78950928e3356163a001dfdbba9f2a432fd5cf4bce7f54ad49d756e6a2acc30aec4226529293
7
- data.tar.gz: 94d9f280bcd1fe4d9d7b9b91c96e149bcd7b0d03dadc3b3023c83ea21a1abd363abaab504c2c504f52ad1471e9e1952fd8d2166ddfb47120086fadd53d7c9bbc
6
+ metadata.gz: d558480f55924d0e55e6028f87186008ed51346ab8cb1720dd2330613fe3c34ec1c6c8c33575d4e9bc228e9a9bd3be2f6826da3349c67ab12a4006c744e5ea49
7
+ data.tar.gz: 75554b9bcb105507d74f18ae96552bc1dd93b1d5b813f0dc5fba1e5cfdbf3ace56a54f25b623a5948ee268c1ea936b87466e87101900efba49da2d94ac763168
data/lib/core_ext.rb CHANGED
@@ -32,4 +32,42 @@ class Array
32
32
  sign = (self.last & 0x80 != 0) ? (-1 ^ ((1 << ((self.size * 8) - 1)) - 1)) : 0
33
33
  sign | self.to_uint
34
34
  end
35
+
36
+ def append_crc16
37
+ append_uint(crc16, 2)
38
+ end
39
+
40
+ def check_crc16(remove_after_check = false)
41
+ orig_crc = pop(2)
42
+ old_crc = (orig_crc[1] << 8) + orig_crc[0]
43
+ new_crc = crc16
44
+ concat(orig_crc) unless remove_after_check
45
+ old_crc == new_crc
46
+ end
47
+
48
+ def xor(array2)
49
+ zip(array2).map{|x, y| x ^ y }
50
+ end
51
+
52
+ def to_bytehex
53
+ map{|x| x.to_bytehex}
54
+ end
55
+
56
+ private
57
+
58
+ def crc16
59
+ crc = 0x6363
60
+ self.each do |byte|
61
+ bb = (byte ^ crc) & 0xFF
62
+ bb = (bb ^ (bb << 4)) & 0xFF
63
+ crc = (crc >> 8) ^ (bb << 8) ^ (bb << 3) ^ (bb >> 4)
64
+ end
65
+ crc & 0xFFFF
66
+ end
67
+ end
68
+
69
+ class Numeric
70
+ def to_bytehex
71
+ self.to_s(16).rjust(2, '0').upcase
72
+ end
35
73
  end
data/lib/exceptions.rb CHANGED
@@ -5,6 +5,7 @@ class IncorrectCRCError < CommunicationError; end
5
5
  class CollisionError < CommunicationError; end
6
6
 
7
7
  class UnexpectedDataError < StandardError; end
8
+ class UsageError < StandardError; end
8
9
 
9
10
  class MifareNakError < StandardError; end
10
11
 
data/lib/mfrc522.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'pi_piper'
1
+ require 'fubuki'
2
2
 
3
3
  require 'openssl'
4
4
  require 'securerandom'
@@ -7,15 +7,14 @@ require 'core_ext'
7
7
  require 'exceptions'
8
8
 
9
9
  require 'picc'
10
- require 'iso144434'
11
10
 
12
11
  require 'mifare/key'
13
12
  require 'mifare/classic'
13
+ require 'mifare/plus'
14
+ require 'mifare/des_fire'
14
15
  require 'mifare/ultralight'
15
16
  require 'mifare/ultralight_c'
16
- require 'mifare/des_fire'
17
-
18
- include PiPiper
17
+ require 'mifare/ultralight_ev1'
19
18
 
20
19
  class MFRC522
21
20
 
@@ -27,8 +26,6 @@ class MFRC522
27
26
  PICC_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2
28
27
  PICC_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3
29
28
  PICC_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
30
- # Mifare Acknowledge
31
- PICC_MF_ACK = 0x0A
32
29
 
33
30
  # PCD commands
34
31
  PCD_Idle = 0x00 # no action, cancels current command execution
@@ -100,19 +97,11 @@ class MFRC522
100
97
  TestDAC2Reg = 0x3A # defines the test value for TestDAC2
101
98
  TestADCReg = 0x3B # shows the value of ADC I and Q channels
102
99
 
103
- def initialize(nrstpd = 24, chip = 0, spd = 8000000, timer = 256)
104
- chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0,
105
- 1 => PiPiper::Spi::CHIP_SELECT_1,
106
- 2 => PiPiper::Spi::CHIP_SELECT_BOTH,
107
- 3 => PiPiper::Spi::CHIP_SELECT_NONE }
108
- @spi_chip = chip_option[chip]
109
- @spi_spd = spd
110
- @timer = timer
111
-
112
- # Power it up
113
- nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out)
114
- nrstpd_pin.on
115
- sleep 1.0 / 20.0 # Wait 50ms
100
+ def initialize(spi_bus: 0, spi_chip: 0, spi_spd: 1_000_000, spi_delay: 1, pcd_timer: 256)
101
+ @spi_driver = Fubuki::SPI.new(spi_bus, spi_chip)
102
+ @spi_speed = spi_spd
103
+ @spi_delay = spi_delay
104
+ @pcd_timer = pcd_timer
116
105
 
117
106
  soft_reset # Perform software reset
118
107
 
@@ -135,6 +124,12 @@ class MFRC522
135
124
 
136
125
  # Reset PCD config to default
137
126
  def pcd_config_reset
127
+ # Stop current command
128
+ write_spi(CommandReg, PCD_Idle)
129
+
130
+ # Stop crypto1 communication
131
+ mifare_crypto1_deauthenticate
132
+
138
133
  # Clear ValuesAfterColl bit
139
134
  write_spi_clear_bitmask(CollReg, 0x80)
140
135
 
@@ -143,7 +138,7 @@ class MFRC522
143
138
  transceiver_baud_rate(:rx, 0)
144
139
 
145
140
  # Set PCD timer value for 302us default timer
146
- internal_timer(@timer)
141
+ internal_timer(@pcd_timer)
147
142
  end
148
143
 
149
144
  # Control transceive timeout value
@@ -159,10 +154,13 @@ class MFRC522
159
154
  # value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
160
155
  def transceiver_baud_rate(direction, value = nil)
161
156
  reg = {tx: TxModeReg, rx: RxModeReg}
157
+ mod = {0 => 0x26, 1 => 0x15, 2 => 0x0A, 3 => 0x05}
162
158
 
163
159
  if value
160
+ @built_in_crc_disabled = (value == 0)
161
+ write_spi(ModWidthReg, mod.fetch(value))
164
162
  value <<= 4
165
- value |= 0x80 if value != 0
163
+ value |= 0x80 unless @built_in_crc_disabled
166
164
  write_spi(reg.fetch(direction), value)
167
165
  end
168
166
 
@@ -189,6 +187,10 @@ class MFRC522
189
187
  (read_spi(RFCfgReg) & 0x70) >> 4
190
188
  end
191
189
 
190
+ def buffer_size
191
+ 64
192
+ end
193
+
192
194
  # Wakes PICC from HALT or IDLE to ACTIVE state
193
195
  # Accept PICC_REQA and PICC_WUPA command
194
196
  def picc_request(picc_command)
@@ -201,7 +203,7 @@ class MFRC522
201
203
 
202
204
  # Instruct PICC in ACTIVE state go to HALT state
203
205
  def picc_halt
204
- buffer = append_crc([PICC_HLTA, 0])
206
+ buffer = [PICC_HLTA, 0].append_crc16
205
207
 
206
208
  status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer)
207
209
 
@@ -213,7 +215,7 @@ class MFRC522
213
215
  # Select PICC for further communication
214
216
  #
215
217
  # PICC must be in state ACTIVE
216
- def picc_select
218
+ def picc_select(disable_anticollision = false)
217
219
  # Description of buffer structure:
218
220
  #
219
221
  # Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
@@ -248,27 +250,42 @@ class MFRC522
248
250
  current_level_known_bits = 0
249
251
  received_data = []
250
252
  valid_bits = 0
253
+ timeout = true
251
254
 
252
- loop do
255
+ # Maxmimum loop count is defined in ISO spec
256
+ 32.times do
253
257
  if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything
254
- # ensure there's nothing weird in buffer
255
- if buffer.size != 6 && !buffer.select{|b| !buffer.is_a?(Fixnum)}.empty?
256
- current_level_known_bits = 0
258
+ # Validate buffer content against non-numeric classes and incorrect size
259
+ buffer = buffer[0..5]
260
+ dirty_buffer = buffer.size != 6
261
+ dirty_buffer ||= buffer.any?{|byte| !byte.is_a?(Integer) }
262
+
263
+ # Retry reading UID when buffer is dirty, but don't reset loop count to prevent infinite loop
264
+ if dirty_buffer
265
+ # Reinitialize all variables
257
266
  buffer = [cascade_level]
267
+ current_level_known_bits = 0
268
+ received_data = []
269
+ valid_bits = 0
270
+
271
+ # Continue to next loop
258
272
  next
259
273
  end
260
274
 
261
275
  tx_last_bits = 0
262
276
  buffer[1] = 0x70 # NVB - We're sending full length byte[0..6]
263
- buffer << (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
277
+ buffer[6] = (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
264
278
 
265
279
  # Append CRC to buffer
266
- buffer = append_crc(buffer)
280
+ buffer.append_crc16
267
281
  else
268
282
  tx_last_bits = current_level_known_bits % 8
269
283
  uid_full_byte = current_level_known_bits / 8
270
284
  all_full_byte = 2 + uid_full_byte # length of SEL + NVB + UID
271
285
  buffer[1] = (all_full_byte << 4) + tx_last_bits # NVB
286
+
287
+ buffer_length = all_full_byte + (tx_last_bits > 0 ? 1 : 0)
288
+ buffer = buffer[0...buffer_length]
272
289
  end
273
290
 
274
291
  framing_bit = (tx_last_bits << 4) + tx_last_bits
@@ -278,10 +295,23 @@ class MFRC522
278
295
 
279
296
  if status != :status_ok && status != :status_collision
280
297
  raise CommunicationError, status
298
+ elsif status == :status_collision && disable_anticollision
299
+ raise CollisionError
300
+ end
301
+
302
+ if received_data.empty?
303
+ raise UnexpectedDataError, 'Received empty UID data'
281
304
  end
282
305
 
283
306
  # Append received UID into buffer if not doing full select
284
- buffer = buffer[0...all_full_byte] + received_data[0..3] if current_level_known_bits < 32
307
+ if current_level_known_bits < 32
308
+ # Check for last collision
309
+ if tx_last_bits != 0
310
+ buffer[-1] |= received_data.shift
311
+ end
312
+
313
+ buffer += received_data
314
+ end
285
315
 
286
316
  # Handle collision
287
317
  if status == :status_collision
@@ -293,18 +323,27 @@ class MFRC522
293
323
  collision_position = collision & 0x1F
294
324
  collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32
295
325
  raise CollisionError if collision_position <= current_level_known_bits
296
-
297
- # Mark the collision bit
326
+
327
+ # Calculate positioin
298
328
  current_level_known_bits = collision_position
299
329
  uid_bit = (current_level_known_bits - 1) % 8
300
- uid_byte = (current_level_known_bits / 8) + (uid_bit != 0 ? 1 : 0)
301
- buffer[1 + uid_byte] |= (1 << uid_bit)
330
+
331
+ # Mark the collision bit
332
+ buffer[-1] |= (1 << uid_bit)
302
333
  else
303
- break if current_level_known_bits >= 32
334
+ if current_level_known_bits >= 32
335
+ timeout = false
336
+ break
337
+ end
304
338
  current_level_known_bits = 32 # We've already known all bits, loop again for a complete select
305
339
  end
306
340
  end
307
341
 
342
+ # Handle timeout after 32 loops
343
+ if timeout
344
+ raise UnexpectedDataError, 'Keep receiving incomplete UID until timeout'
345
+ end
346
+
308
347
  # We've finished current cascade level
309
348
  # Check and collect all uid stored in buffer
310
349
 
@@ -315,7 +354,7 @@ class MFRC522
315
354
  # Check the result of full select
316
355
  # Select Acknowledge is 1 byte + CRC16
317
356
  raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0
318
- raise IncorrectCRCError unless check_crc(received_data)
357
+ raise IncorrectCRCError unless received_data.check_crc16(true)
319
358
 
320
359
  sak = received_data[0]
321
360
  break if (sak & 0x04) == 0 # No more cascade level
@@ -339,54 +378,6 @@ class MFRC522
339
378
  status && uid == new_uid
340
379
  end
341
380
 
342
- # Lookup PICC name using sak
343
- def identify_model(sak)
344
- # SAK coding separation reference:
345
- # http://cache.nxp.com/documents/application_note/AN10833.pdf
346
- # http://www.nxp.com/documents/application_note/130830.pdf
347
- if sak & 0x04 != 0
348
- return :picc_uid_not_complete
349
- end
350
-
351
- if sak & 0x02 != 0
352
- return :picc_reserved_future_use
353
- end
354
-
355
- if sak & 0x08 != 0
356
- if sak & 0x10 != 0
357
- return :picc_mifare_4k
358
- end
359
-
360
- if sak & 0x01 != 0
361
- return :picc_mifare_mini
362
- end
363
-
364
- return :picc_mifare_1k
365
- end
366
-
367
- if sak & 0x10 != 0
368
- if sak & 0x01 != 0
369
- return :picc_mifare_plus_4k_sl2
370
- end
371
-
372
- return :picc_mifare_plus_2k_sl2
373
- end
374
-
375
- if sak == 0x00
376
- return :picc_mifare_ultralight
377
- end
378
-
379
- if sak & 0x20 != 0
380
- return :picc_iso_14443_4
381
- end
382
-
383
- if sak & 0x40 != 0
384
- return :picc_iso_18092
385
- end
386
-
387
- return :picc_unknown
388
- end
389
-
390
381
  # Start Crypto1 communication between reader and Mifare PICC
391
382
  #
392
383
  # PICC must be selected before calling for authentication
@@ -414,60 +405,55 @@ class MFRC522
414
405
 
415
406
  # Append CRC to buffer and check CRC or Mifare acknowledge
416
407
  def picc_transceive(send_data, accept_timeout = false)
417
- send_data = append_crc(send_data)
408
+ send_data = send_data.dup
409
+ send_data.append_crc16 if @built_in_crc_disabled
418
410
 
419
- puts "Sending Data: #{send_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
411
+ puts "Sending Data: #{send_data.to_bytehex}" if ENV['DEBUG']
420
412
 
421
413
  # Transfer data
422
414
  status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
423
- return [] if status == :status_picc_timeout && accept_timeout
415
+ return if status == :status_picc_timeout && accept_timeout
424
416
  raise PICCTimeoutError if status == :status_picc_timeout
425
417
  raise CommunicationError, status if status != :status_ok
426
418
 
427
- puts "Received Data: #{received_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
428
-
429
- # Data exists, check CRC and return
430
- if received_data.size > 1
431
- raise IncorrectCRCError unless check_crc(received_data)
419
+ puts "Received Data: #{received_data.to_bytehex}" if ENV['DEBUG']
420
+ puts "Valid bits: #{valid_bits}" if ENV['DEBUG']
432
421
 
433
- return received_data[0..-3]
422
+ # Data exists, check CRC
423
+ if received_data.size > 2 && @built_in_crc_disabled
424
+ raise IncorrectCRCError unless received_data.check_crc16(true)
434
425
  end
435
426
 
436
- raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
437
- raise MifareNakError, received_data[0] if received_data[0] != PICC_MF_ACK
438
-
439
- received_data
427
+ return received_data, valid_bits
440
428
  end
441
429
 
442
- private
443
-
444
430
  # Read from SPI communication
445
431
  def read_spi(reg)
446
- output = 0
447
- PiPiper::Spi.begin do |spi|
448
- spi.chip_select_active_low(true)
449
- spi.bit_order Spi::MSBFIRST
450
- spi.clock @spi_spd
451
-
452
- spi.chip_select(@spi_chip) do
453
- spi.write((reg << 1) & 0x7E | 0x80)
454
- output = spi.read
455
- end
456
- end
457
- output
432
+ read_spi_bulk(reg).first
433
+ end
434
+
435
+ def read_spi_bulk(*regs)
436
+ regs.flatten!
437
+
438
+ payload = regs.map{ |reg| ((reg & 0x3F) << 1) | 0x80 }
439
+ payload << 0x00
440
+
441
+ result = @spi_driver.transfer(payload, @spi_speed, @spi_delay)
442
+
443
+ # discard first byte
444
+ result.shift
445
+
446
+ result
458
447
  end
459
448
 
460
449
  # Write to SPI communication
461
450
  def write_spi(reg, values)
462
- PiPiper::Spi.begin do |spi|
463
- spi.chip_select_active_low(true)
464
- spi.bit_order Spi::MSBFIRST
465
- spi.clock @spi_spd
451
+ spi_addr = (reg & 0x3F) << 1
452
+ payload = [spi_addr, *values]
466
453
 
467
- spi.chip_select(@spi_chip) do
468
- spi.write((reg << 1) & 0x7E, *values)
469
- end
470
- end
454
+ @spi_driver.transfer(payload, @spi_speed, @spi_delay)
455
+
456
+ true
471
457
  end
472
458
 
473
459
  # Set bits by mask
@@ -510,15 +496,16 @@ class MFRC522
510
496
 
511
497
  # Check for error
512
498
  error = read_spi(ErrorReg)
513
- return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
499
+ return :status_buffer_overflow if (error & 0x10) != 0
500
+ return :status_crc_error if (error & 0x04) != 0
501
+ return :status_parity_error if (error & 0x02) != 0
502
+ return :status_protocol_error if (error & 0x01) != 0
514
503
 
515
504
  # Receiving data
516
505
  received_data = []
517
506
  data_length = read_spi(FIFOLevelReg)
518
- while data_length > 0 do
519
- data = read_spi(FIFODataReg)
520
- received_data << data
521
- data_length -=1
507
+ if data_length > 0
508
+ received_data = read_spi_bulk(Array.new(data_length, FIFODataReg))
522
509
  end
523
510
  valid_bits = read_spi(ControlReg) & 0x07
524
511
 
@@ -527,39 +514,4 @@ class MFRC522
527
514
 
528
515
  return status, received_data, valid_bits
529
516
  end
530
-
531
- def calculate_crc(data)
532
- write_spi(CommandReg, PCD_Idle) # Stop any active command.
533
- write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
534
- write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
535
- write_spi(FIFODataReg, data) # Write data to the FIFO
536
- write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
537
-
538
- # Wait for the command to complete
539
- i = 5000
540
- loop do
541
- irq = read_spi(DivIrqReg)
542
- break if (irq & 0x04) != 0
543
- raise PCDTimeoutError, 'Error calculating CRC' if i == 0
544
- i -= 1
545
- end
546
-
547
- write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
548
-
549
- [read_spi(CRCResultRegL), read_spi(CRCResultRegH)]
550
- end
551
-
552
- def append_crc(data)
553
- data + calculate_crc(data)
554
- end
555
-
556
- def check_crc(data)
557
- raise UnexpectedDataError, 'Data too short for CRC check' if data.size < 3
558
-
559
- data = data.dup
560
- crc = data.pop(2)
561
-
562
- crc == calculate_crc(data)
563
- end
564
-
565
517
  end