10static const char *
const TAG =
"pn532.mifare_ultralight";
17 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
21 ESP_LOGW(TAG,
"Not NDEF formatted");
22 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
25 uint8_t message_length;
26 uint8_t message_start_index;
28 ESP_LOGW(TAG,
"Couldn't find NDEF message");
29 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
31 ESP_LOGVV(TAG,
"NDEF message length: %u, start: %u", message_length, message_start_index);
33 if (message_length == 0) {
34 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
37 const uint8_t read_length = message_length + message_start_index > 12 ? message_length + message_start_index - 12 : 0;
40 ESP_LOGE(TAG,
"Error reading tag data");
41 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
45 const size_t skip = message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
46 if (skip >= data.size()) {
47 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
50 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2,
51 make_unique<nfc::NdefMessage>(std::span<const uint8_t>(data).subspan(skip)));
57 for (uint8_t i = 0; i * MIFARE_READ_SIZE < num_bytes; i++) {
59 if (!this->
mifare_read_(uint8_t(i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page), chunk)) {
63 const uint16_t remaining = num_bytes - i * MIFARE_READ_SIZE;
64 const size_t count = std::min<size_t>(MIFARE_READ_SIZE, remaining);
65 for (
const uint8_t
byte : std::span<const uint8_t>(chunk).subspan(0,
count)) {
70 char data_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
77 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
79 return (page_3_to_6.size() > p4_offset + 3) &&
80 ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
81 (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
87 ESP_LOGV(TAG,
"Tag capacity is %u bytes", data[2] * 8U);
94 uint8_t &message_start_index) {
95 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
97 if (!(page_3_to_6.size() > p4_offset + 6)) {
101 if (page_3_to_6[p4_offset + 0] == 0x03) {
102 message_length = page_3_to_6[p4_offset + 1];
103 message_start_index = 2;
105 }
else if (page_3_to_6[p4_offset + 5] == 0x03) {
106 message_length = page_3_to_6[p4_offset + 6];
107 message_start_index = 7;
116 const auto encoded =
message->encode();
120 ESP_LOGE(TAG,
"Message length exceeds tag capacity %" PRIu32
" > %" PRIu32, buffer_length,
capacity);
128 uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
130 while (index < buffer_length) {
132 current_page, std::span<const uint8_t>(&
buffer[index], nfc::MIFARE_ULTRALIGHT_PAGE_SIZE))) {
135 index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
143 uint8_t pages = (
capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
145 static constexpr std::array<uint8_t, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE> BLANK_DATA = {0x00, 0x00, 0x00, 0x00};
147 for (
int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) {
157 PN532_COMMAND_INDATAEXCHANGE,
159 nfc::MIFARE_CMD_WRITE_ULTRALIGHT,
168 ESP_LOGE(TAG,
"Error writing page %u", page_num);
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
void push_back(const T &value)
uint16_t read_mifare_ultralight_capacity_()
bool mifare_read_(uint8_t address, MifareReadData &data)
Sends MIFARE READ for address and returns the 16 bytes the tag answers with.
bool read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, UltralightReadBuffer &data)
std::unique_ptr< nfc::NfcTag > read_mifare_ultralight_tag_(nfc::NfcTagUid &uid)
bool find_mifare_ultralight_ndef_(std::span< const uint8_t > page_3_to_6, uint8_t &message_length, uint8_t &message_start_index)
bool write_mifare_ultralight_page_(uint8_t page_num, std::span< const uint8_t > write_data)
bool clean_mifare_ultralight_()
bool write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message)
virtual bool write_data(std::span< const uint8_t > data)=0
bool in_data_exchange_(std::span< const uint8_t > command, PN532Frame &response)
Sends an InDataExchange command and reads the response; returns false unless the status byte reports ...
bool is_mifare_ultralight_formatted_(std::span< const uint8_t > page_3_to_6)
const LogString * message
char * format_bytes_to(char *buffer, std::span< const uint8_t > bytes)
Format bytes to buffer with ' ' separator (e.g., "04 11 22 33"). Returns buffer for inline use.
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length)
void fill_ndef_tlv(const std::span< const uint8_t > message, const uint32_t buffer_length, FixedVector< uint8_t > &buffer)
Fills buffer with the NDEF TLV (type, length, message, terminator) padded with zeros to buffer_length
std::array< uint8_t, MIFARE_READ_SIZE > MifareReadData
uint16_t uint16_t & capacity