diff --git a/adafruit_pn532/adafruit_pn532.py b/adafruit_pn532/adafruit_pn532.py index 513dcec..d6a8620 100644 --- a/adafruit_pn532/adafruit_pn532.py +++ b/adafruit_pn532/adafruit_pn532.py @@ -27,6 +27,7 @@ Implementation Notes """ import time +import struct from digitalio import Direction from micropython import const @@ -501,6 +502,101 @@ class PN532: ) return response[0] == 0x0 + def mifare_classic_sub_value_block(self, block_number: int, amount: int) -> bool: + """Decrease the balance of a value block. Block number should be the block + to change and amount should be an integer up to a maximum of 2147483647. + If the value block is successfully updated then True is returned, + otherwise False is returned. + """ + params = [0x01, MIFARE_CMD_DECREMENT, block_number & 0xFF] + params.extend(list(amount.to_bytes(4, "little"))) + + response = self.call_function( + _COMMAND_INDATAEXCHANGE, params=params, response_length=1 + ) + if response[0] != 0x00: + return False + + response = self.call_function( + _COMMAND_INDATAEXCHANGE, + params=[0x01, MIFARE_CMD_TRANSFER, block_number & 0xFF], + response_length=1, + ) + + return response[0] == 0x00 + + def mifare_classic_add_value_block(self, block_number: int, amount: int) -> bool: + """Increase the balance of a value block. Block number should be the block + to change and amount should be an integer up to a maximum of 2147483647. + If the value block is successfully updated then True is returned, + otherwise False is returned. + """ + params = [0x01, MIFARE_CMD_INCREMENT, block_number & 0xFF] + params.extend(list(amount.to_bytes(4, "little"))) + + response = self.call_function( + _COMMAND_INDATAEXCHANGE, params=params, response_length=1 + ) + if response[0] != 0x00: + return False + + response = self.call_function( + _COMMAND_INDATAEXCHANGE, + params=[0x01, MIFARE_CMD_TRANSFER, block_number & 0xFF], + response_length=1, + ) + + return response[0] == 0x00 + + def mifare_classic_get_value_block(self, block_number: int) -> int: + """Read the contents of a value block and return a integer representing the + current balance. Block number should be the block to read. + """ + block = self.mifare_classic_read_block(block_number=block_number) + if block is None: + return None + + value = block[0:4] + value_inverted = block[4:8] + value_backup = block[8:12] + if value != value_backup: + raise RuntimeError( + "Value block bytes 0-3 do not match 8-11: " + + "".join("%02x" % b for b in block) + ) + if value_inverted != bytearray(map((lambda x: x ^ 0xFF), value)): + raise RuntimeError( + "Inverted value block bytes 4-7 not valid: " + + "".join("%02x" % b for b in block) + ) + + return struct.unpack(" bool: + """Formats a block on the card so it is suitable for use as a value block. + Block number should be the block to use. Initial value should be an integer + up to a maximum of 2147483647. Address block is optional and can be used + as part of backup management. + """ + data = bytearray() + initial_value = initial_value.to_bytes(4, "little") + # Value + data.extend(initial_value) + # Inverted value + data.extend(bytearray(map((lambda x: x ^ 0xFF), initial_value))) + # Duplicate of value + data.extend(initial_value) + + # Address + address_block = address_block.to_bytes(1, "little")[0] + data.extend( + [address_block, address_block ^ 0xFF, address_block, address_block ^ 0xFF] + ) + + return self.mifare_classic_write_block(block_number, data) + def ntag2xx_write_block(self, block_number: int, data: ReadableBuffer) -> bool: """Write a block of data to the card. Block number should be the block to write and data should be a byte array of length 4 with the data to diff --git a/examples/pn532_value_block_mifare.py b/examples/pn532_value_block_mifare.py new file mode 100644 index 0000000..201d97d --- /dev/null +++ b/examples/pn532_value_block_mifare.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2015 Tony DiCola, Roberto Laricchia, +# and Francesco Crisafulli, for Adafruit Industries + +# SPDX-License-Identifier: MIT + +# Example of detecting and reading a value block from a MiFare classic NFC card. + +""" +This example shows connecting to the PN532 and writing & reading a mifare classic +type RFID tag +""" + +import board +import busio + +# Additional import needed for I2C/SPI +# from digitalio import DigitalInOut +# +# NOTE: pick the import that matches the interface being used +# +from adafruit_pn532.adafruit_pn532 import MIFARE_CMD_AUTH_B +from adafruit_pn532.i2c import PN532_I2C + +# from adafruit_pn532.spi import PN532_SPI +# from adafruit_pn532.uart import PN532_UART + +# I2C connection: +i2c = busio.I2C(board.SCL, board.SDA) + +# Non-hardware reset/request with I2C +pn532 = PN532_I2C(i2c, debug=False) + +# With I2C, we recommend connecting RSTPD_N (reset) to a digital pin for manual +# harware reset +# reset_pin = DigitalInOut(board.D6) +# On Raspberry Pi, you must also connect a pin to P32 "H_Request" for hardware +# wakeup! this means we don't need to do the I2C clock-stretch thing +# req_pin = DigitalInOut(board.D12) +# pn532 = PN532_I2C(i2c, debug=False, reset=reset_pin, req=req_pin) + +# SPI connection: +# spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +# cs_pin = DigitalInOut(board.D5) +# pn532 = PN532_SPI(spi, cs_pin, debug=False) + +# UART connection +# uart = busio.UART(board.TX, board.RX, baudrate=115200, timeout=0.1) +# pn532 = PN532_UART(uart, debug=False) + +ic, ver, rev, support = pn532.firmware_version +print("Found PN532 with firmware version: {0}.{1}".format(ver, rev)) + +# Configure PN532 to communicate with MiFare cards +pn532.SAM_configuration() + +print("Waiting for RFID/NFC card to write to!") + +key_a = b"\xFF\xFF\xFF\xFF\xFF\xFF" +key_b = b"\xFF\xFF\xFF\xFF\xFF\xFF" + + +while True: + # Check if a card is available to read + uid = pn532.read_passive_target(timeout=0.5) + print(".", end="") + # Try again if no card is available. + if uid is not None: + break + +print("") + +print("Found card with UID:", [hex(i) for i in uid]) +print("Authenticating block 4 ...") + +authenticated = pn532.mifare_classic_authenticate_block( + uid, 4, MIFARE_CMD_AUTH_B, key_b +) +if not authenticated: + print("Authentication failed!") + +# Format block and set initial balance of 100 +response = pn532.mifare_classic_fmt_value_block(4, 100) +print( + response, + "Formatted block 4, balance is:", + pn532.mifare_classic_get_value_block(4), +) + +response = pn532.mifare_classic_sub_value_block(4, 50) +print( + response, + "Decrease by 50, new balance:", + pn532.mifare_classic_get_value_block(4), +) + +response = pn532.mifare_classic_add_value_block(4, 1337) +print( + response, + "Increase by 1337, new balance:", + pn532.mifare_classic_get_value_block(4), +)