ESPHome  2024.5.0
pn7150_mifare_ultralight.cpp
Go to the documentation of this file.
1 #include <cinttypes>
2 #include <memory>
3 
4 #include "pn7150.h"
5 #include "esphome/core/log.h"
6 
7 namespace esphome {
8 namespace pn7150 {
9 
10 static const char *const TAG = "pn7150.mifare_ultralight";
11 
13  std::vector<uint8_t> data;
14  // pages 3 to 6 contain various info we are interested in -- do one read to grab it all
15  if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE * nfc::MIFARE_ULTRALIGHT_READ_SIZE,
16  data) != nfc::STATUS_OK) {
17  return nfc::STATUS_FAILED;
18  }
19 
20  if (!this->is_mifare_ultralight_formatted_(data)) {
21  ESP_LOGW(TAG, "Not NDEF formatted");
22  return nfc::STATUS_FAILED;
23  }
24 
25  uint8_t message_length;
26  uint8_t message_start_index;
27  if (this->find_mifare_ultralight_ndef_(data, message_length, message_start_index) != nfc::STATUS_OK) {
28  ESP_LOGW(TAG, "Couldn't find NDEF message");
29  return nfc::STATUS_FAILED;
30  }
31  ESP_LOGVV(TAG, "NDEF message length: %u, start: %u", message_length, message_start_index);
32 
33  if (message_length == 0) {
34  return nfc::STATUS_FAILED;
35  }
36  // we already read pages 3-6 earlier -- pick up where we left off so we're not re-reading pages
37  const uint8_t read_length = message_length + message_start_index > 12 ? message_length + message_start_index - 12 : 0;
38  if (read_length) {
39  if (read_mifare_ultralight_bytes_(nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE + 3, read_length, data) !=
40  nfc::STATUS_OK) {
41  ESP_LOGE(TAG, "Error reading tag data");
42  return nfc::STATUS_FAILED;
43  }
44  }
45  // we need to trim off page 3 as well as any bytes ahead of message_start_index
46  data.erase(data.begin(), data.begin() + message_start_index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
47 
48  tag.set_ndef_message(make_unique<nfc::NdefMessage>(data));
49 
50  return nfc::STATUS_OK;
51 }
52 
53 uint8_t PN7150::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data) {
54  const uint8_t read_increment = nfc::MIFARE_ULTRALIGHT_READ_SIZE * nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
55  nfc::NciMessage rx;
56  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, {nfc::MIFARE_CMD_READ, start_page});
57 
58  for (size_t i = 0; i * read_increment < num_bytes; i++) {
59  tx.get_message().back() = i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page;
60  do { // loop because sometimes we struggle here...???...
61  if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
62  ESP_LOGE(TAG, "Error reading tag data");
63  return nfc::STATUS_FAILED;
64  }
65  } while (rx.get_payload_size() < read_increment);
66  uint16_t bytes_offset = (i + 1) * read_increment;
67  auto pages_in_end_itr = bytes_offset <= num_bytes ? rx.get_message().end() - 1
68  : rx.get_message().end() - (bytes_offset - num_bytes + 1);
69 
70  if ((pages_in_end_itr > rx.get_message().begin()) && (pages_in_end_itr < rx.get_message().end())) {
71  data.insert(data.end(), rx.get_message().begin() + nfc::NCI_PKT_HEADER_SIZE, pages_in_end_itr);
72  }
73  }
74 
75  ESP_LOGVV(TAG, "Data read: %s", nfc::format_bytes(data).c_str());
76 
77  return nfc::STATUS_OK;
78 }
79 
80 bool PN7150::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6) {
81  const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
82 
83  return (page_3_to_6.size() > p4_offset + 3) &&
84  !((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) &&
85  (page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF));
86 }
87 
89  std::vector<uint8_t> data;
90  if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE, data) == nfc::STATUS_OK) {
91  ESP_LOGV(TAG, "Tag capacity is %u bytes", data[2] * 8U);
92  return data[2] * 8U;
93  }
94  return 0;
95 }
96 
97 uint8_t PN7150::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
98  uint8_t &message_start_index) {
99  const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector
100 
101  if (!(page_3_to_6.size() > p4_offset + 5)) {
102  return nfc::STATUS_FAILED;
103  }
104 
105  if (page_3_to_6[p4_offset + 0] == 0x03) {
106  message_length = page_3_to_6[p4_offset + 1];
107  message_start_index = 2;
108  return nfc::STATUS_OK;
109  } else if (page_3_to_6[p4_offset + 5] == 0x03) {
110  message_length = page_3_to_6[p4_offset + 6];
111  message_start_index = 7;
112  return nfc::STATUS_OK;
113  }
114  return nfc::STATUS_FAILED;
115 }
116 
117 uint8_t PN7150::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid,
118  const std::shared_ptr<nfc::NdefMessage> &message) {
119  uint32_t capacity = this->read_mifare_ultralight_capacity_();
120 
121  auto encoded = message->encode();
122 
123  uint32_t message_length = encoded.size();
124  uint32_t buffer_length = nfc::get_mifare_ultralight_buffer_size(message_length);
125 
126  if (buffer_length > capacity) {
127  ESP_LOGE(TAG, "Message length exceeds tag capacity %" PRIu32 " > %" PRIu32, buffer_length, capacity);
128  return nfc::STATUS_FAILED;
129  }
130 
131  encoded.insert(encoded.begin(), 0x03);
132  if (message_length < 255) {
133  encoded.insert(encoded.begin() + 1, message_length);
134  } else {
135  encoded.insert(encoded.begin() + 1, 0xFF);
136  encoded.insert(encoded.begin() + 2, (message_length >> 8) & 0xFF);
137  encoded.insert(encoded.begin() + 2, message_length & 0xFF);
138  }
139  encoded.push_back(0xFE);
140 
141  encoded.resize(buffer_length, 0);
142 
143  uint32_t index = 0;
144  uint8_t current_page = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
145 
146  while (index < buffer_length) {
147  std::vector<uint8_t> data(encoded.begin() + index, encoded.begin() + index + nfc::MIFARE_ULTRALIGHT_PAGE_SIZE);
148  if (this->write_mifare_ultralight_page_(current_page, data) != nfc::STATUS_OK) {
149  return nfc::STATUS_FAILED;
150  }
151  index += nfc::MIFARE_ULTRALIGHT_PAGE_SIZE;
152  current_page++;
153  }
154  return nfc::STATUS_OK;
155 }
156 
158  uint32_t capacity = this->read_mifare_ultralight_capacity_();
159  uint8_t pages = (capacity / nfc::MIFARE_ULTRALIGHT_PAGE_SIZE) + nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE;
160 
161  std::vector<uint8_t> blank_data = {0x00, 0x00, 0x00, 0x00};
162 
163  for (int i = nfc::MIFARE_ULTRALIGHT_DATA_START_PAGE; i < pages; i++) {
164  if (this->write_mifare_ultralight_page_(i, blank_data) != nfc::STATUS_OK) {
165  return nfc::STATUS_FAILED;
166  }
167  }
168  return nfc::STATUS_OK;
169 }
170 
171 uint8_t PN7150::write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data) {
172  std::vector<uint8_t> payload = {nfc::MIFARE_CMD_WRITE_ULTRALIGHT, page_num};
173  payload.insert(payload.end(), write_data.begin(), write_data.end());
174 
175  nfc::NciMessage rx;
176  nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, payload);
177 
178  if (this->transceive_(tx, rx, NFCC_TAG_WRITE_TIMEOUT) != nfc::STATUS_OK) {
179  ESP_LOGE(TAG, "Error writing page %u", page_num);
180  return nfc::STATUS_FAILED;
181  }
182  return nfc::STATUS_OK;
183 }
184 
185 } // namespace pn7150
186 } // namespace esphome
uint8_t write_mifare_ultralight_page_(uint8_t page_num, std::vector< uint8_t > &write_data)
uint8_t write_mifare_ultralight_tag_(std::vector< uint8_t > &uid, const std::shared_ptr< nfc::NdefMessage > &message)
void set_ndef_message(std::unique_ptr< NdefMessage > ndef_message)
Definition: nfc_tag.h:48
uint8_t find_mifare_ultralight_ndef_(const std::vector< uint8_t > &page_3_to_6, uint8_t &message_length, uint8_t &message_start_index)
uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout=NFCC_DEFAULT_TIMEOUT, bool expect_notification=true)
Definition: pn7150.cpp:1074
bool is_mifare_ultralight_formatted_(const std::vector< uint8_t > &page_3_to_6)
uint8_t read_mifare_ultralight_tag_(nfc::NfcTag &tag)
std::vector< uint8_t > & get_message()
Definition: nci_message.cpp:67
uint8_t get_payload_size(bool recompute=false)
Definition: nci_message.cpp:43
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
uint8_t read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector< uint8_t > &data)
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