ESPHome 2025.5.0
Loading...
Searching...
No Matches
pn532_mifare_ultralight.cpp
Go to the documentation of this file.
1#include <memory>
2
3#include "pn532.h"
4#include "esphome/core/log.h"
5
6namespace esphome {
7namespace pn532 {
8
9static const char *const TAG = "pn532.mifare_ultralight";
10
11std::unique_ptr<nfc::NfcTag> PN532::read_mifare_ultralight_tag_(std::vector<uint8_t> &uid) {
12 std::vector<uint8_t> data;
13 // pages 3 to 6 contain various info we are interested in -- do one read to grab it all
14 if (!this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE * nfc::MIFARE_ULTRALIGHT_READ_SIZE,
15 data)) {
16 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
17 }
18
19 if (!this->is_mifare_ultralight_formatted_(data)) {
20 ESP_LOGW(TAG, "Not NDEF formatted");
21 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
22 }
23
24 uint8_t message_length;
25 uint8_t message_start_index;
26 if (!this->find_mifare_ultralight_ndef_(data, message_length, message_start_index)) {
27 ESP_LOGW(TAG, "Couldn't find NDEF message");
28 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
29 }
30 ESP_LOGVV(TAG, "NDEF message length: %u, start: %u", message_length, message_start_index);
31
32 if (message_length == 0) {
33 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
34 }
35 // we already read pages 3-6 earlier -- pick up where we left off so we're not re-reading pages
36 const uint8_t read_length = message_length + message_start_index > 12 ? message_length + message_start_index - 12 : 0;
37 if (read_length) {
38 if (!read_mifare_ultralight_bytes_(nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE + 3, read_length, data)) {
39 ESP_LOGE(TAG, "Error reading tag data");
40 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
41 }
42 }
43 // we need to trim off page 3 as well as any bytes ahead of message_start_index
44 data.erase(data.begin(), data.begin() + message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
45
46 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2, data);
47}
48
49bool PN532::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data) {
50 const uint8_t read_increment = nfc::MIFARE_ULTRALIGHT_READ_SIZE * nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
51 std::vector<uint8_t> response;
52
53 for (uint8_t i = 0; i * read_increment < num_bytes; i++) {
54 if (!this->write_command_({
55 PN532_COMMAND_INDATAEXCHANGE,
56 0x01, // One card
57 nfc::MIFARE_CMD_READ,
58 uint8_t(i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page),
59 })) {
60 return false;
61 }
62
63 if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) {
64 return false;
65 }
66 uint16_t bytes_offset = (i + 1) * read_increment;
67 auto pages_in_end_itr = bytes_offset <= num_bytes ? response.end() : response.end() - (bytes_offset - num_bytes);
68
69 if ((pages_in_end_itr > response.begin()) && (pages_in_end_itr <= response.end())) {
70 data.insert(data.end(), response.begin() + 1, pages_in_end_itr);
71 }
72 }
73
74 ESP_LOGVV(TAG, "Data read: %s", nfc::format_bytes(data).c_str());
75
76 return true;
77}
78
79bool PN532::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6) {
80 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
81
82 return (page_3_to_6.size() > p4_offset + 3) &&
83 ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) ||
84 (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF));
85}
86
88 std::vector<uint8_t> data;
89 if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE, data)) {
90 ESP_LOGV(TAG, "Tag capacity is %u bytes", data[2] * 8U);
91 return data[2] * 8U;
92 }
93 return 0;
94}
95
96bool PN532::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
97 uint8_t &message_start_index) {
98 const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
99
100 if (!(page_3_to_6.size() > p4_offset + 5)) {
101 return false;
102 }
103
104 if (page_3_to_6[p4_offset + 0] == 0x03) {
105 message_length = page_3_to_6[p4_offset + 1];
106 message_start_index = 2;
107 return true;
108 } else if (page_3_to_6[p4_offset + 5] == 0x03) {
109 message_length = page_3_to_6[p4_offset + 6];
110 message_start_index = 7;
111 return true;
112 }
113 return false;
114}
115
116bool PN532::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
117 uint32_t capacity = this->read_mifare_ultralight_capacity_();
118
119 auto encoded = message->encode();
120
121 uint32_t message_length = encoded.size();
122 uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(message_length);
123
124 if (buffer_length > capacity) {
125 ESP_LOGE(TAG, "Message length exceeds tag capacity %" PRIu32 " > %" PRIu32, buffer_length, capacity);
126 return false;
127 }
128
129 encoded.insert(encoded.begin(), 0x03);
130 if (message_length < 255) {
131 encoded.insert(encoded.begin() + 1, message_length);
132 } else {
133 encoded.insert(encoded.begin() + 1, 0xFF);
134 encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
135 encoded.insert(encoded.begin() + 2, message_length & 0xFF);
136 }
137 encoded.push_back(0xFE);
138
139 encoded.resize(buffer_length, 0);
140
141 uint32_t index = 0;
142 uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
143
144 while (index < buffer_length) {
145 std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
146 if (!this->write_mifare_ultralight_page_(current_page, data)) {
147 return false;
148 }
149 index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
150 current_page++;
151 }
152 return true;
153}
154
156 uint32_t capacity = this->read_mifare_ultralight_capacity_();
157 uint8_t pages = (capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
158
159 std::vector<uint8_t> blank_data = {0x00, 0x00, 0x00, 0x00};
160
161 for (int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) {
162 if (!this->write_mifare_ultralight_page_(i, blank_data)) {
163 return false;
164 }
165 }
166 return true;
167}
168
169bool PN532::write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data) {
170 std::vector<uint8_t> data({
171 PN532_COMMAND_INDATAEXCHANGE,
172 0x01, // One card
173 nfc::MIFARE_CMD_WRITE_ULTRALIGHT,
174 page_num,
175 });
176 data.insert(data.end(), write_data.begin(), write_data.end());
177 if (!this->write_command_(data)) {
178 ESP_LOGE(TAG, "Error writing page %u", page_num);
179 return false;
180 }
181
182 std::vector<uint8_t> response;
183 if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) {
184 ESP_LOGE(TAG, "Error writing page %u", page_num);
185 return false;
186 }
187
188 return true;
189}
190
191} // namespace pn532
192} // namespace esphome
std::vector< uint8_t > encode()
virtual bool write_data(const std::vector< uint8_t > &data)=0
virtual bool read_response(uint8_t command, std::vector< uint8_t > &data)=0
bool find_mifare_ultralight_ndef_(const std::vector< uint8_t > &page_3_to_6, uint8_t &message_length, uint8_t &message_start_index)
bool is_mifare_ultralight_formatted_(const std::vector< uint8_t > &page_3_to_6)
bool write_mifare_ultralight_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
bool read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector< uint8_t > &data)
std::unique_ptr< nfc::NfcTag > read_mifare_ultralight_tag_(std::vector< uint8_t > &uid)
bool write_mifare_ultralight_page_(uint8_t page_num, std::vector< uint8_t > &write_data)
bool write_command_(const std::vector< uint8_t > &data)
Definition pn532.cpp:246
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length)
Definition nfc.cpp:71
std::string format_bytes(std::vector< uint8_t > &bytes)
Definition nfc.cpp:22
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::unique_ptr< T > make_unique(Args &&...args)
Definition helpers.h:85