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);
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: %" PRIu32
", start: %u", message_length, message_start_index);
33 if (message_length == 0) {
34 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
39 const uint32_t total_length = message_length + message_start_index;
41 ESP_LOGW(TAG,
"NDEF message of %" PRIu32
" bytes at offset %u exceeds tag capacity %" PRIu32, message_length,
43 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
45 if (message_length > MIFARE_ULTRALIGHT_MAX_NDEF_SIZE) {
46 ESP_LOGE(TAG,
"NDEF message too long: %" PRIu32
" bytes", message_length);
47 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
51 constexpr size_t header_message_bytes = MIFARE_READ_SIZE - nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
52 const uint16_t read_length =
53 total_length > header_message_bytes ?
static_cast<uint16_t
>(total_length - header_message_bytes) : 0;
56 std::span<uint8_t>
message(
buffer.get(), data.size() + read_length);
57 std::copy(data.begin(), data.end(),
message.begin());
60 message.subspan(data.size()))) {
61 ESP_LOGE(TAG,
"Error reading tag data");
62 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
66 const size_t skip = message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
68 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
71 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2,
72 make_unique<nfc::NdefMessage>(std::span<const uint8_t>(
message).subspan(skip)));
78 for (uint8_t i = 0; i * MIFARE_READ_SIZE < data.size(); i++) {
80 if (!this->
mifare_read_(uint8_t(i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page), chunk)) {
84 const size_t offset = i * MIFARE_READ_SIZE;
85 const size_t count = std::min(MIFARE_READ_SIZE, data.size() - offset);
86 std::copy_n(chunk.begin(),
count, data.begin() + offset);
89 char data_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
96 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
98 return (page_3_to_6.size() > p4_offset + 3) &&
99 ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
100 (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
104 std::array<uint8_t, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE> data;
106 ESP_LOGV(TAG,
"Tag capacity is %u bytes", data[2] * 8U);
113 uint8_t &message_start_index) {
114 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
116 if (!(page_3_to_6.size() > p4_offset + 6)) {
121 if (page_3_to_6[p4_offset + 0] == 0x03) {
123 }
else if (page_3_to_6[p4_offset + 5] == 0x03) {
131 if (page_3_to_6[p4_offset + tlv_offset + 1] == 0xFF) {
132 if (page_3_to_6.size() <= p4_offset + tlv_offset + 3) {
138 (
static_cast<uint32_t>(page_3_to_6[p4_offset + tlv_offset + 2]) << 8) | page_3_to_6[p4_offset + tlv_offset + 3];
139 message_start_index = tlv_offset + 4;
141 message_length = page_3_to_6[p4_offset + tlv_offset + 1];
142 message_start_index = tlv_offset + 2;
150 const auto encoded =
message->encode();
154 ESP_LOGE(TAG,
"Message length exceeds tag capacity %" PRIu32
" > %" PRIu32, buffer_length,
capacity);
162 uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
164 while (index < buffer_length) {
166 current_page, std::span<const uint8_t>(&
buffer[index], nfc::MIFARE_ULTRALIGHT_PAGE_SIZE))) {
169 index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
177 uint8_t pages = (
capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
179 static constexpr std::array<uint8_t, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE> BLANK_DATA = {0x00, 0x00, 0x00, 0x00};
181 for (
int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) {
191 PN532_COMMAND_INDATAEXCHANGE,
193 nfc::MIFARE_CMD_WRITE_ULTRALIGHT,
202 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...
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
void push_back(const T &value)
bool find_mifare_ultralight_ndef_(std::span< const uint8_t > page_3_to_6, uint32_t &message_length, uint8_t &message_start_index)
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.
std::unique_ptr< nfc::NfcTag > read_mifare_ultralight_tag_(nfc::NfcTagUid &uid)
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)
bool read_mifare_ultralight_bytes_(uint8_t start_page, std::span< uint8_t > data)
Fills data with the bytes from start_page onward.
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
ESPHOME_LOG_TAG(TAG, "pn532")
std::array< uint8_t, MIFARE_READ_SIZE > MifareReadData
std::array< uint8_t, MIFARE_READ_SIZE > UltralightHeader
Pages 3 to 6, read in one go: the capability container and the start of the NDEF TLV.
uint16_t uint16_t & capacity