mfrc522 1.0.2 → 3.0.0

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