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
-
#initialize(nrstpd = 24, chip = 0, spd = 1000000, 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.
-
#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.
Constructor Details
#initialize(nrstpd = 24, chip = 0, spd = 1000000, timer = 256) ⇒ MFRC522
shows the value of ADC I and Q channels
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/mfrc522.rb', line 102 def initialize(nrstpd = 24, chip = 0, spd = 1000000, timer = 256) chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0, 1 => PiPiper::Spi::CHIP_SELECT_1, 2 => PiPiper::Spi::CHIP_SELECT_BOTH, 3 => PiPiper::Spi::CHIP_SELECT_NONE } @spi_chip = chip_option[chip] @spi_spd = spd @timer = timer # Power it up nrstpd_pin = PiPiper::Pin.new(pin: nrstpd, direction: :out) nrstpd_pin.on sleep 1.0 / 20.0 # Wait 50ms 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
192 193 194 195 196 197 198 |
# File 'lib/mfrc522.rb', line 192 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
186 187 188 |
# File 'lib/mfrc522.rb', line 186 def antenna_off write_spi_clear_bitmask(TxControlReg, 0x03) end |
#antenna_on ⇒ Object
Turn antenna on
181 182 183 |
# File 'lib/mfrc522.rb', line 181 def antenna_on write_spi_set_bitmask(TxControlReg, 0x03) end |
#buffer_size ⇒ Object
200 201 202 |
# File 'lib/mfrc522.rb', line 200 def buffer_size 64 end |
#internal_timer(timer = nil) ⇒ Object
Control transceive timeout value
155 156 157 158 159 160 161 |
# File 'lib/mfrc522.rb', line 155 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
405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/mfrc522.rb', line 405 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
418 419 420 |
# File 'lib/mfrc522.rb', line 418 def mifare_crypto1_deauthenticate write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit end |
#pcd_config_reset ⇒ Object
Reset PCD config to default
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/mfrc522.rb', line 136 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(@timer) end |
#picc_halt ⇒ Object
Instruct PICC in ACTIVE state go to HALT state
215 216 217 218 219 220 221 222 223 |
# File 'lib/mfrc522.rb', line 215 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
206 207 208 209 210 211 212 |
# File 'lib/mfrc522.rb', line 206 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
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 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/mfrc522.rb', line 228 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? do |byte| if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0') !byte.is_a?(Numeric) else !byte.is_a?(Fixnum) end end # 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
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
# File 'lib/mfrc522.rb', line 423 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 |
#reestablish_picc_communication(uid) ⇒ Object
Trying to restart picc
383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'lib/mfrc522.rb', line 383 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
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/mfrc522.rb', line 124 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
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/mfrc522.rb', line 165 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 |