ESPHome  2024.11.2
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 
6 namespace esphome {
7 namespace pn532 {
8 
9 static const char *const TAG = "pn532.mifare_ultralight";
10 
11 std::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 
49 bool 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 
79 bool 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 
96 bool 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 
116 bool 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 
169 bool 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
virtual bool write_data(const std::vector< uint8_t > &data)=0
bool is_mifare_ultralight_formatted_(const std::vector< uint8_t > &page_3_to_6)
virtual bool read_response(uint8_t command, std::vector< uint8_t > &data)=0
bool read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector< uint8_t > &data)
bool find_mifare_ultralight_ndef_(const std::vector< uint8_t > &page_3_to_6, uint8_t &message_length, uint8_t &message_start_index)
std::unique_ptr< nfc::NfcTag > read_mifare_ultralight_tag_(std::vector< uint8_t > &uid)
std::vector< uint8_t > encode()
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
bool write_mifare_ultralight_page_(uint8_t page_num, std::vector< uint8_t > &write_data)
bool write_mifare_ultralight_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
uint32_t get_mifare_ultralight_buffer_size(uint32_t message_length)
Definition: nfc.cpp:71
bool write_command_(const std::vector< uint8_t > &data)
Definition: pn532.cpp:246
std::string format_bytes(std::vector< uint8_t > &bytes)
Definition: nfc.cpp:22