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