Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions adafruit_pn532/adafruit_pn532.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"""

import time
import struct
from digitalio import Direction
from micropython import const

Expand Down Expand Up @@ -501,6 +502,101 @@ def mifare_classic_write_block(
)
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("<i", value)[0]

def mifare_classic_fmt_value_block(
self, block_number: int, initial_value: int, address_block: int = 0
) -> 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
Expand Down
101 changes: 101 additions & 0 deletions examples/pn532_value_block_mifare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# SPDX-FileCopyrightText: <text> 2015 Tony DiCola, Roberto Laricchia,
# and Francesco Crisafulli, for Adafruit Industries </text>

# 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),
)