Class: MFRC522
- Inherits:
-
Object
- Object
- MFRC522
- Defined in:
- lib/mfrc522.rb
Constant Summary collapse
- PICC_REQA =
PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
0x26
- PICC_WUPA =
REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame.
0x52
- PICC_CT =
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.
0x88
- PICC_SEL_CL1 =
Cascade Tag. Not really a command, but used during anti collision.
0x93
- PICC_SEL_CL2 =
Anti collision/Select, Cascade Level 1
0x95
- PICC_SEL_CL3 =
Anti collision/Select, Cascade Level 2
0x97
- PICC_HLTA =
Anti collision/Select, Cascade Level 3
0x50
- PCD_Idle =
PCD commands
0x00
- PCD_Mem =
no action, cancels current command execution
0x01
- PCD_GenRandomID =
stores 25 bytes into the internal buffer
0x02
- PCD_CalcCRC =
generates a 10-byte random ID number
0x03
- PCD_Transmit =
activates the CRC coprocessor or performs a self test
0x04
- PCD_NoCmdChange =
transmits data from the FIFO buffer
0x07
- PCD_Receive =
no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit
0x08
- PCD_Transceive =
activates the receiver circuits
0x0C
- PCD_MFAuthent =
transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission
0x0E
- PCD_SoftReset =
performs the MIFARE standard authentication as a reader
0x0F
- CommandReg =
PCD Command and Status Registers
0x01
- ComIEnReg =
starts and stops command execution
0x02
- DivIEnReg =
enable and disable interrupt request control bits
0x03
- ComIrqReg =
enable and disable interrupt request control bits
0x04
- DivIrqReg =
interrupt request bits
0x05
- ErrorReg =
interrupt request bits
0x06
- Status1Reg =
error bits showing the error status of the last command executed
0x07
- Status2Reg =
communication status bits
0x08
- FIFODataReg =
receiver and transmitter status bits
0x09
- FIFOLevelReg =
input and output of 64 byte FIFO buffer
0x0A
- WaterLevelReg =
number of bytes stored in the FIFO buffer
0x0B
- ControlReg =
level for FIFO underflow and overflow warning
0x0C
- BitFramingReg =
miscellaneous control registers
0x0D
- CollReg =
adjustments for bit-oriented frames
0x0E
- ModeReg =
PCD Command Registers
0x11
- TxModeReg =
defines general modes for transmitting and receiving
0x12
- RxModeReg =
defines transmission data rate and framing
0x13
- TxControlReg =
defines reception data rate and framing
0x14
- TxASKReg =
controls the logical behavior of the antenna driver pins TX1 and TX2
0x15
- TxSelReg =
controls the setting of the transmission modulation
0x16
- RxSelReg =
selects the internal sources for the antenna driver
0x17
- RxThresholdReg =
selects internal receiver settings
0x18
- DemodReg =
selects thresholds for the bit decoder
0x19
- MfTxReg =
defines demodulator settings
0x1C
- MfRxReg =
controls some MIFARE communication transmit parameters
0x1D
- SerialSpeedReg =
controls some MIFARE communication receive parameters
0x1F
- CRCResultRegH =
PCD Configuration Registers
0x21
- CRCResultRegL =
shows the MSB and LSB values of the CRC calculation
0x22
- ModWidthReg =
controls the ModWidth setting?
0x24
- RFCfgReg =
configures the receiver gain
0x26
- GsNReg =
selects the conductance of the antenna driver pins TX1 and TX2 for modulation
0x27
- CWGsPReg =
defines the conductance of the p-driver output during periods of no modulation
0x28
- ModGsPReg =
defines the conductance of the p-driver output during periods of modulation
0x29
- TModeReg =
defines settings for the internal timer
0x2A
- TPrescalerReg =
the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg.
0x2B
- TReloadRegH =
defines the 16-bit timer reload value
0x2C
- TReloadRegL =
0x2D
- TCounterValueRegH =
shows the 16-bit timer value
0x2E
- TCounterValueRegL =
0x2F
- TestSel1Reg =
PCD Test Registers
0x31
- TestSel2Reg =
general test signal configuration
0x32
- TestPinEnReg =
general test signal configuration
0x33
- TestPinValueReg =
enables pin output driver on pins D1 to D7
0x34
- TestBusReg =
defines the values for D1 to D7 when it is used as an I/O bus
0x35
- AutoTestReg =
shows the status of the internal test bus
0x36
- VersionReg =
controls the digital self test
0x37
- AnalogTestReg =
shows the software version
0x38
- TestDAC1Reg =
controls the pins AUX1 and AUX2
0x39
- TestDAC2Reg =
defines the test value for TestDAC1
0x3A
- TestADCReg =
defines the test value for TestDAC2
0x3B
Instance Method Summary collapse
-
#antenna_gain(level = nil) ⇒ Object
Modify and show antenna gain level level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB.
-
#antenna_off ⇒ Object
Turn antenna off.
-
#antenna_on ⇒ Object
Turn antenna on.
- #buffer_size ⇒ Object
-
#communicate_with_picc(command, send_data, framing_bit = 0) ⇒ Object
PCD transceive helper.
-
#initialize(spi_bus: 0, spi_chip: 0, spi_spd: 1_000_000, spi_delay: 1, pcd_timer: 256) ⇒ MFRC522
constructor
shows the value of ADC I and Q channels.
-
#internal_timer(timer = nil) ⇒ Object
Control transceive timeout value.
-
#mifare_crypto1_authenticate(command, block_addr, sector_key, uid) ⇒ Object
Start Crypto1 communication between reader and Mifare PICC.
-
#mifare_crypto1_deauthenticate ⇒ Object
Stop Crypto1 communication.
-
#pcd_config_reset ⇒ Object
Reset PCD config to default.
-
#picc_halt ⇒ Object
Instruct PICC in ACTIVE state go to HALT state.
-
#picc_request(picc_command) ⇒ Object
Wakes PICC from HALT or IDLE to ACTIVE state Accept PICC_REQA and PICC_WUPA command.
-
#picc_select(disable_anticollision = false) ⇒ Object
Select PICC for further communication.
-
#picc_transceive(send_data, accept_timeout = false) ⇒ Object
Append CRC to buffer and check CRC or Mifare acknowledge.
-
#read_spi(reg) ⇒ Object
Read from SPI communication.
- #read_spi_bulk(*regs) ⇒ Object
-
#reestablish_picc_communication(uid) ⇒ Object
Trying to restart picc.
-
#soft_reset ⇒ Object
PCD software reset.
-
#transceiver_baud_rate(direction, value = nil) ⇒ Object
Control transceiver baud rate value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd.
-
#write_spi(reg, values) ⇒ Object
Write to SPI communication.
-
#write_spi_clear_bitmask(reg, mask) ⇒ Object
Clear bits by mask.
-
#write_spi_set_bitmask(reg, mask) ⇒ Object
Set bits by mask.
Constructor Details
#initialize(spi_bus: 0, spi_chip: 0, spi_spd: 1_000_000, spi_delay: 1, pcd_timer: 256) ⇒ MFRC522
shows the value of ADC I and Q channels
100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/mfrc522.rb', line 100 def initialize(spi_bus: 0, spi_chip: 0, spi_spd: 1_000_000, spi_delay: 1, pcd_timer: 256) @spi_driver = Fubuki::SPI.new(spi_bus, spi_chip) @spi_speed = spi_spd @spi_delay = spi_delay @pcd_timer = pcd_timer soft_reset # Perform software reset pcd_config_reset # Set default setting antenna_on # Turn antenna on. They were disabled by the reset. end |
Instance Method Details
#antenna_gain(level = nil) ⇒ Object
Modify and show antenna gain level level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB
182 183 184 185 186 187 188 |
# File 'lib/mfrc522.rb', line 182 def antenna_gain(level = nil) unless level.nil? level = 1 if level > 6 || level < 1 write_spi_set_bitmask(RFCfgReg, ((level + 1) << 4)) end (read_spi(RFCfgReg) & 0x70) >> 4 end |
#antenna_off ⇒ Object
Turn antenna off
176 177 178 |
# File 'lib/mfrc522.rb', line 176 def antenna_off write_spi_clear_bitmask(TxControlReg, 0x03) end |
#antenna_on ⇒ Object
Turn antenna on
171 172 173 |
# File 'lib/mfrc522.rb', line 171 def antenna_on write_spi_set_bitmask(TxControlReg, 0x03) end |
#buffer_size ⇒ Object
190 191 192 |
# File 'lib/mfrc522.rb', line 190 def buffer_size 64 end |
#communicate_with_picc(command, send_data, framing_bit = 0) ⇒ Object
PCD transceive helper
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'lib/mfrc522.rb', line 472 def communicate_with_picc(command, send_data, framing_bit = 0) wait_irq = 0x00 wait_irq = 0x10 if command == PCD_MFAuthent wait_irq = 0x30 if command == PCD_Transceive write_spi(CommandReg, PCD_Idle) # Stop any active command. write_spi(ComIrqReg, 0x7F) # Clear all seven interrupt request bits write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization write_spi(FIFODataReg, send_data) # Write sendData to the FIFO write_spi(BitFramingReg, framing_bit) # Bit adjustments write_spi(CommandReg, command) # Execute the command if command == PCD_Transceive write_spi_set_bitmask(BitFramingReg, 0x80) # StartSend=1, transmission of data starts end # Wait for the command to complete i = 2000 loop do irq = read_spi(ComIrqReg) break if (irq & wait_irq) != 0 return :status_picc_timeout if (irq & 0x01) != 0 return :status_pcd_timeout if i == 0 i -= 1 end # Check for error error = read_spi(ErrorReg) return :status_buffer_overflow if (error & 0x10) != 0 return :status_crc_error if (error & 0x04) != 0 return :status_parity_error if (error & 0x02) != 0 return :status_protocol_error if (error & 0x01) != 0 # Receiving data received_data = [] data_length = read_spi(FIFOLevelReg) if data_length > 0 received_data = read_spi_bulk(Array.new(data_length, FIFODataReg)) end valid_bits = read_spi(ControlReg) & 0x07 status = :status_ok status = :status_collision if (error & 0x08) != 0 # CollErr return status, received_data, valid_bits end |
#internal_timer(timer = nil) ⇒ Object
Control transceive timeout value
145 146 147 148 149 150 151 |
# File 'lib/mfrc522.rb', line 145 def internal_timer(timer = nil) if timer write_spi(TReloadRegH, (timer >> 8) & 0xFF) write_spi(TReloadRegL, (timer & 0xFF)) end (read_spi(TReloadRegH) << 8) | read_spi(TReloadRegL) end |
#mifare_crypto1_authenticate(command, block_addr, sector_key, uid) ⇒ Object
Start Crypto1 communication between reader and Mifare PICC
PICC must be selected before calling for authentication Remember to deauthenticate after communication, or no new communication can be made
Accept PICC_MF_AUTH_KEY_A or PICC_MF_AUTH_KEY_B command Checks datasheets for block address numbering of your PICC
389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/mfrc522.rb', line 389 def mifare_crypto1_authenticate(command, block_addr, sector_key, uid) # Buffer[12]: {command, block_addr, sector_key[6], uid[4]} buffer = [command, block_addr] buffer.concat(sector_key[0..5]) buffer.concat(uid[0..3]) communicate_with_picc(PCD_MFAuthent, buffer) # Check MFCrypto1On bit (read_spi(Status2Reg) & 0x08) != 0 end |
#mifare_crypto1_deauthenticate ⇒ Object
Stop Crypto1 communication
402 403 404 |
# File 'lib/mfrc522.rb', line 402 def mifare_crypto1_deauthenticate write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit end |
#pcd_config_reset ⇒ Object
Reset PCD config to default
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/mfrc522.rb', line 126 def pcd_config_reset # Stop current command write_spi(CommandReg, PCD_Idle) # Stop crypto1 communication mifare_crypto1_deauthenticate # Clear ValuesAfterColl bit write_spi_clear_bitmask(CollReg, 0x80) # Reset transceiver baud rate to 106 kBd transceiver_baud_rate(:tx, 0) transceiver_baud_rate(:rx, 0) # Set PCD timer value for 302us default timer internal_timer(@pcd_timer) end |
#picc_halt ⇒ Object
Instruct PICC in ACTIVE state go to HALT state
205 206 207 208 209 210 211 212 213 |
# File 'lib/mfrc522.rb', line 205 def picc_halt buffer = [PICC_HLTA, 0].append_crc16 status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer) # PICC in HALT state will not respond # If PICC sent reply, means it didn't acknowledge the command we sent status == :status_picc_timeout end |
#picc_request(picc_command) ⇒ Object
Wakes PICC from HALT or IDLE to ACTIVE state Accept PICC_REQA and PICC_WUPA command
196 197 198 199 200 201 202 |
# File 'lib/mfrc522.rb', line 196 def picc_request(picc_command) pcd_config_reset status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07) status == :status_ok && valid_bits == 0 # REQA or WUPA command return 16 bits(full byte) end |
#picc_select(disable_anticollision = false) ⇒ Object
Select PICC for further communication
PICC must be in state ACTIVE
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/mfrc522.rb', line 218 def picc_select(disable_anticollision = false) # Description of buffer structure: # # Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 # Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. # Byte 2: UID-data or Cascade Tag # Byte 3: UID-data # Byte 4: UID-data # Byte 5: UID-data # Byte 6: Block Check Character - XOR of bytes 2-5 # Byte 7: CRC_A # Byte 8: CRC_A # The BCC and CRC_A are only transmitted if we know all the UID bits of the current Cascade Level. # # Description of bytes 2-5 # # UID size Cascade level Byte2 Byte3 Byte4 Byte5 # ======== ============= ===== ===== ===== ===== # 4 bytes 1 uid0 uid1 uid2 uid3 # 7 bytes 1 CT uid0 uid1 uid2 # 2 uid3 uid4 uid5 uid6 # 10 bytes 1 CT uid0 uid1 uid2 # 2 CT uid3 uid4 uid5 # 3 uid6 uid7 uid8 uid9 pcd_config_reset cascade_levels = [PICC_SEL_CL1, PICC_SEL_CL2, PICC_SEL_CL3] uid = [] sak = 0 cascade_levels.each do |cascade_level| buffer = [cascade_level] current_level_known_bits = 0 received_data = [] valid_bits = 0 timeout = true # Maxmimum loop count is defined in ISO spec 32.times do if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything # Validate buffer content against non-numeric classes and incorrect size buffer = buffer[0..5] dirty_buffer = buffer.size != 6 dirty_buffer ||= buffer.any?{|byte| !byte.is_a?(Integer) } # Retry reading UID when buffer is dirty, but don't reset loop count to prevent infinite loop if dirty_buffer # Reinitialize all variables buffer = [cascade_level] current_level_known_bits = 0 received_data = [] valid_bits = 0 # Continue to next loop next end tx_last_bits = 0 buffer[1] = 0x70 # NVB - We're sending full length byte[0..6] buffer[6] = (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character # Append CRC to buffer buffer.append_crc16 else tx_last_bits = current_level_known_bits % 8 uid_full_byte = current_level_known_bits / 8 all_full_byte = 2 + uid_full_byte # length of SEL + NVB + UID buffer[1] = (all_full_byte << 4) + tx_last_bits # NVB buffer_length = all_full_byte + (tx_last_bits > 0 ? 1 : 0) buffer = buffer[0...buffer_length] end framing_bit = (tx_last_bits << 4) + tx_last_bits # Select it status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, buffer, framing_bit) if status != :status_ok && status != :status_collision raise CommunicationError, status elsif status == :status_collision && disable_anticollision raise CollisionError end if received_data.empty? raise UnexpectedDataError, 'Received empty UID data' end # Append received UID into buffer if not doing full select if current_level_known_bits < 32 # Check for last collision if tx_last_bits != 0 buffer[-1] |= received_data.shift end buffer += received_data end # Handle collision if status == :status_collision collision = read_spi(CollReg) # CollPosNotValid - We don't know where collision happened raise CollisionError if (collision & 0x20) != 0 collision_position = collision & 0x1F collision_position = 32 if collision_position == 0 # Values 0-31, 0 means bit 32 raise CollisionError if collision_position <= current_level_known_bits # Calculate positioin current_level_known_bits = collision_position uid_bit = (current_level_known_bits - 1) % 8 # Mark the collision bit buffer[-1] |= (1 << uid_bit) else if current_level_known_bits >= 32 timeout = false break end current_level_known_bits = 32 # We've already known all bits, loop again for a complete select end end # Handle timeout after 32 loops if timeout raise UnexpectedDataError, 'Keep receiving incomplete UID until timeout' end # We've finished current cascade level # Check and collect all uid stored in buffer # Append UID uid << buffer[2] if buffer[2] != PICC_CT uid << buffer[3] << buffer[4] << buffer[5] # Check the result of full select # Select Acknowledge is 1 byte + CRC16 raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0 raise IncorrectCRCError unless received_data.check_crc16(true) sak = received_data[0] break if (sak & 0x04) == 0 # No more cascade level end return uid, sak end |
#picc_transceive(send_data, accept_timeout = false) ⇒ Object
Append CRC to buffer and check CRC or Mifare acknowledge
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/mfrc522.rb', line 407 def picc_transceive(send_data, accept_timeout = false) send_data = send_data.dup send_data.append_crc16 if @built_in_crc_disabled puts "Sending Data: #{send_data.to_bytehex}" if ENV['DEBUG'] # Transfer data status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data) return if status == :status_picc_timeout && accept_timeout raise PICCTimeoutError if status == :status_picc_timeout raise CommunicationError, status if status != :status_ok puts "Received Data: #{received_data.to_bytehex}" if ENV['DEBUG'] puts "Valid bits: #{valid_bits}" if ENV['DEBUG'] # Data exists, check CRC if received_data.size > 2 && @built_in_crc_disabled raise IncorrectCRCError unless received_data.check_crc16(true) end return received_data, valid_bits end |
#read_spi(reg) ⇒ Object
Read from SPI communication
431 432 433 |
# File 'lib/mfrc522.rb', line 431 def read_spi(reg) read_spi_bulk(reg).first end |
#read_spi_bulk(*regs) ⇒ Object
435 436 437 438 439 440 441 442 443 444 445 446 447 |
# File 'lib/mfrc522.rb', line 435 def read_spi_bulk(*regs) regs.flatten! payload = regs.map{ |reg| ((reg & 0x3F) << 1) | 0x80 } payload << 0x00 result = @spi_driver.transfer(payload, @spi_speed, @spi_delay) # discard first byte result.shift result end |
#reestablish_picc_communication(uid) ⇒ Object
Trying to restart picc
367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/mfrc522.rb', line 367 def reestablish_picc_communication(uid) picc_halt picc_request(PICC_WUPA) begin new_uid, _new_sak = picc_select status = true rescue CommunicationError status = false end status && uid == new_uid end |
#soft_reset ⇒ Object
PCD software reset
114 115 116 117 118 119 120 121 122 123 |
# File 'lib/mfrc522.rb', line 114 def soft_reset write_spi(CommandReg, PCD_SoftReset) sleep 1.0 / 20.0 # wait 50ms write_spi(TModeReg, 0x87) # Start timer by setting TAuto=1, and higher part of TPrescalerReg write_spi(TPrescalerReg, 0xFF) # Set lower part of TPrescalerReg, and results in 302us timer (f_timer = 13.56 MHz / (2*TPreScaler+1)) write_spi(TxASKReg, 0x40) # Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting 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) end |
#transceiver_baud_rate(direction, value = nil) ⇒ Object
Control transceiver baud rate value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/mfrc522.rb', line 155 def transceiver_baud_rate(direction, value = nil) reg = {tx: TxModeReg, rx: RxModeReg} mod = {0 => 0x26, 1 => 0x15, 2 => 0x0A, 3 => 0x05} if value @built_in_crc_disabled = (value == 0) write_spi(ModWidthReg, mod.fetch(value)) value <<= 4 value |= 0x80 unless @built_in_crc_disabled write_spi(reg.fetch(direction), value) end (read_spi(reg.fetch(direction)) >> 4) & 0x07 end |
#write_spi(reg, values) ⇒ Object
Write to SPI communication
450 451 452 453 454 455 456 457 |
# File 'lib/mfrc522.rb', line 450 def write_spi(reg, values) spi_addr = (reg & 0x3F) << 1 payload = [spi_addr, *values] @spi_driver.transfer(payload, @spi_speed, @spi_delay) true end |
#write_spi_clear_bitmask(reg, mask) ⇒ Object
Clear bits by mask
466 467 468 469 |
# File 'lib/mfrc522.rb', line 466 def write_spi_clear_bitmask(reg, mask) value = read_spi(reg) write_spi(reg, value & (~mask)) end |
#write_spi_set_bitmask(reg, mask) ⇒ Object
Set bits by mask
460 461 462 463 |
# File 'lib/mfrc522.rb', line 460 def write_spi_set_bitmask(reg, mask) value = read_spi(reg) write_spi(reg, value | mask) end |