ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
pn532.cpp
Go to the documentation of this file.
1#include "pn532.h"
2
3#include <memory>
4#include "esphome/core/log.h"
5#include "esphome/core/hal.h"
6
7// Based on:
8// - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf
9// - https://www.nxp.com/docs/en/nxp/application-notes/AN133910.pdf
10// - https://www.nxp.com/docs/en/nxp/application-notes/153710.pdf
11
12namespace esphome {
13namespace pn532 {
14
15static const char *const TAG = "pn532";
16
18 // Get version data
19 if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
20 ESP_LOGW(TAG, "Error sending version command, trying again");
21 if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) {
22 ESP_LOGE(TAG, "Error sending version command");
23 this->mark_failed();
24 return;
25 }
26 }
27
28 std::vector<uint8_t> version_data;
29 if (!this->read_response(PN532_COMMAND_VERSION_DATA, version_data)) {
30 ESP_LOGE(TAG, "Error getting version");
31 this->mark_failed();
32 return;
33 }
34 ESP_LOGD(TAG, "Found chip PN5%02X", version_data[0]);
35 ESP_LOGD(TAG, "Firmware ver. %d.%d", version_data[1], version_data[2]);
36
37 if (!this->write_command_({
38 PN532_COMMAND_SAMCONFIGURATION,
39 0x01, // normal mode
40 0x14, // zero timeout (not in virtual card mode)
41 0x01,
42 })) {
43 ESP_LOGE(TAG, "No wakeup ack");
44 this->mark_failed();
45 return;
46 }
47
48 std::vector<uint8_t> wakeup_result;
49 if (!this->read_response(PN532_COMMAND_SAMCONFIGURATION, wakeup_result)) {
50 this->error_code_ = WAKEUP_FAILED;
51 this->mark_failed();
52 return;
53 }
54
55 // Set up SAM (secure access module)
56 uint8_t sam_timeout = std::min<uint8_t>(255u, this->update_interval_ / 50);
57 if (!this->write_command_({
58 PN532_COMMAND_SAMCONFIGURATION,
59 0x01, // normal mode
60 sam_timeout, // timeout as multiple of 50ms (actually only for virtual card mode, but shouldn't matter)
61 0x01, // Enable IRQ
62 })) {
63 this->error_code_ = SAM_COMMAND_FAILED;
64 this->mark_failed();
65 return;
66 }
67
68 std::vector<uint8_t> sam_result;
69 if (!this->read_response(PN532_COMMAND_SAMCONFIGURATION, sam_result)) {
70 ESP_LOGV(TAG, "Invalid SAM result: (%u)", sam_result.size()); // NOLINT
71 for (uint8_t dat : sam_result) {
72 ESP_LOGV(TAG, " 0x%02X", dat);
73 }
74 this->error_code_ = SAM_COMMAND_FAILED;
75 this->mark_failed();
76 return;
77 }
78
79 this->turn_off_rf_();
80}
81
83 updates_enabled_ = false;
84 requested_read_ = false;
85 ESP_LOGI(TAG, "Powering down PN532");
86 if (!this->write_command_({PN532_COMMAND_POWERDOWN, 0b10100000})) { // enable i2c,spi wakeup
87 ESP_LOGE(TAG, "Error writing powerdown command to PN532");
88 return false;
89 }
90 std::vector<uint8_t> response;
91 if (!this->read_response(PN532_COMMAND_POWERDOWN, response)) {
92 ESP_LOGE(TAG, "Error reading PN532 powerdown response");
93 return false;
94 }
95 if (response[0] != 0x00) {
96 ESP_LOGE(TAG, "Error on PN532 powerdown: %02x", response[0]);
97 return false;
98 }
99 ESP_LOGV(TAG, "Powerdown successful");
100 delay(1);
101 return true;
102}
103
105 if (!updates_enabled_)
106 return;
107
108 for (auto *obj : this->binary_sensors_)
109 obj->on_scan_end();
110
111 if (!this->write_command_({
112 PN532_COMMAND_INLISTPASSIVETARGET,
113 0x01, // max 1 card
114 0x00, // baud rate ISO14443A (106 kbit/s)
115 })) {
116 ESP_LOGW(TAG, "Requesting tag read failed!");
117 this->status_set_warning();
118 return;
119 }
120 this->status_clear_warning();
121 this->requested_read_ = true;
122}
123
125 if (!this->requested_read_)
126 return;
127
128 auto ready = this->read_ready_(false);
129 if (ready == WOULDBLOCK)
130 return;
131
132 bool success = false;
133 std::vector<uint8_t> read;
134
135 if (ready == READY) {
136 success = this->read_response(PN532_COMMAND_INLISTPASSIVETARGET, read);
137 } else {
138 this->send_ack_(); // abort still running InListPassiveTarget
139 }
140
141 this->requested_read_ = false;
142
143 if (!success) {
144 // Something failed
145 if (!this->current_uid_.empty()) {
146 auto tag = make_unique<nfc::NfcTag>(this->current_uid_);
147 for (auto *trigger : this->triggers_ontagremoved_)
148 trigger->process(tag);
149 }
150 this->current_uid_ = {};
151 this->turn_off_rf_();
152 return;
153 }
154
155 uint8_t num_targets = read[0];
156 if (num_targets != 1) {
157 // no tags found or too many
158 if (!this->current_uid_.empty()) {
159 auto tag = make_unique<nfc::NfcTag>(this->current_uid_);
160 for (auto *trigger : this->triggers_ontagremoved_)
161 trigger->process(tag);
162 }
163 this->current_uid_ = {};
164 this->turn_off_rf_();
165 return;
166 }
167
168 uint8_t nfcid_length = read[5];
169 std::vector<uint8_t> nfcid(read.begin() + 6, read.begin() + 6 + nfcid_length);
170 if (read.size() < 6U + nfcid_length) {
171 // oops, pn532 returned invalid data
172 return;
173 }
174
175 bool report = true;
176 for (auto *bin_sens : this->binary_sensors_) {
177 if (bin_sens->process(nfcid)) {
178 report = false;
179 }
180 }
181
182 if (nfcid.size() == this->current_uid_.size()) {
183 bool same_uid = true;
184 for (size_t i = 0; i < nfcid.size(); i++)
185 same_uid &= nfcid[i] == this->current_uid_[i];
186 if (same_uid)
187 return;
188 }
189
190 this->current_uid_ = nfcid;
191
192 if (next_task_ == READ) {
193 auto tag = this->read_tag_(nfcid);
194 for (auto *trigger : this->triggers_ontag_)
195 trigger->process(tag);
196
197 if (report) {
198 ESP_LOGD(TAG, "Found new tag '%s'", nfc::format_uid(nfcid).c_str());
199 if (tag->has_ndef_message()) {
200 const auto &message = tag->get_ndef_message();
201 const auto &records = message->get_records();
202 ESP_LOGD(TAG, " NDEF formatted records:");
203 for (const auto &record : records) {
204 ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str());
205 }
206 }
207 }
208 } else if (next_task_ == CLEAN) {
209 ESP_LOGD(TAG, " Tag cleaning");
210 if (!this->clean_tag_(nfcid)) {
211 ESP_LOGE(TAG, " Tag was not fully cleaned successfully");
212 }
213 ESP_LOGD(TAG, " Tag cleaned!");
214 } else if (next_task_ == FORMAT) {
215 ESP_LOGD(TAG, " Tag formatting");
216 if (!this->format_tag_(nfcid)) {
217 ESP_LOGE(TAG, "Error formatting tag as NDEF");
218 }
219 ESP_LOGD(TAG, " Tag formatted!");
220 } else if (next_task_ == WRITE) {
221 if (this->next_task_message_to_write_ != nullptr) {
222 ESP_LOGD(TAG, " Tag writing");
223 ESP_LOGD(TAG, " Tag formatting");
224 if (!this->format_tag_(nfcid)) {
225 ESP_LOGE(TAG, " Tag could not be formatted for writing");
226 } else {
227 ESP_LOGD(TAG, " Writing NDEF data");
228 if (!this->write_tag_(nfcid, this->next_task_message_to_write_)) {
229 ESP_LOGE(TAG, " Failed to write message to tag");
230 }
231 ESP_LOGD(TAG, " Finished writing NDEF data");
232 delete this->next_task_message_to_write_;
233 this->next_task_message_to_write_ = nullptr;
234 this->on_finished_write_callback_.call();
235 }
236 }
237 }
238
239 this->read_mode();
240
241 this->turn_off_rf_();
242}
243
244bool PN532::write_command_(const std::vector<uint8_t> &data) {
245 std::vector<uint8_t> write_data;
246 // Preamble
247 write_data.push_back(0x00);
248
249 // Start code
250 write_data.push_back(0x00);
251 write_data.push_back(0xFF);
252
253 // Length of message, TFI + data bytes
254 const uint8_t real_length = data.size() + 1;
255 // LEN
256 write_data.push_back(real_length);
257 // LCS (Length checksum)
258 write_data.push_back(~real_length + 1);
259
260 // TFI (Frame Identifier, 0xD4 means to PN532, 0xD5 means from PN532)
261 write_data.push_back(0xD4);
262 // calculate checksum, TFI is part of checksum
263 uint8_t checksum = 0xD4;
264
265 // DATA
266 for (uint8_t dat : data) {
267 write_data.push_back(dat);
268 checksum += dat;
269 }
270
271 // DCS (Data checksum)
272 write_data.push_back(~checksum + 1);
273 // Postamble
274 write_data.push_back(0x00);
275
276 this->write_data(write_data);
277
278 return this->read_ack_();
279}
280
282 ESP_LOGV(TAG, "Reading ACK");
283
284 std::vector<uint8_t> data;
285 if (!this->read_data(data, 6)) {
286 return false;
287 }
288
289 bool matches = (data[1] == 0x00 && // preamble
290 data[2] == 0x00 && // start of packet
291 data[3] == 0xFF && data[4] == 0x00 && // ACK packet code
292 data[5] == 0xFF && data[6] == 0x00); // postamble
293 ESP_LOGV(TAG, "ACK valid: %s", YESNO(matches));
294 return matches;
295}
296
298 ESP_LOGV(TAG, "Sending ACK for abort");
299 this->write_data({0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00});
300 delay(10);
301}
303 ESP_LOGV(TAG, "Sending NACK for retransmit");
304 this->write_data({0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00});
305 delay(10);
306}
307
309 if (this->rd_ready_ == READY) {
310 if (block) {
311 this->rd_start_time_ = 0;
312 this->rd_ready_ = WOULDBLOCK;
313 }
314 return READY;
315 }
316
317 if (!this->rd_start_time_) {
318 this->rd_start_time_ = millis();
319 }
320
321 while (true) {
322 if (this->is_read_ready()) {
323 this->rd_ready_ = READY;
324 break;
325 }
326
327 if (millis() - this->rd_start_time_ > 100) {
328 ESP_LOGV(TAG, "Timed out waiting for readiness from PN532!");
329 this->rd_ready_ = TIMEOUT;
330 break;
331 }
332
333 if (!block) {
334 this->rd_ready_ = WOULDBLOCK;
335 break;
336 }
337
338 yield();
339 }
340
341 auto rdy = this->rd_ready_;
342 if (block || rdy == TIMEOUT) {
343 this->rd_start_time_ = 0;
344 this->rd_ready_ = WOULDBLOCK;
345 }
346 return rdy;
347}
348
350 ESP_LOGV(TAG, "Turning RF field OFF");
351 this->write_command_({
352 PN532_COMMAND_RFCONFIGURATION,
353 0x01, // RF Field
354 0x00, // Off
355 });
356}
357
358std::unique_ptr<nfc::NfcTag> PN532::read_tag_(std::vector<uint8_t> &uid) {
359 uint8_t type = nfc::guess_tag_type(uid.size());
360
361 if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
362 ESP_LOGD(TAG, "Mifare classic");
363 return this->read_mifare_classic_tag_(uid);
364 } else if (type == nfc::TAG_TYPE_2) {
365 ESP_LOGD(TAG, "Mifare ultralight");
366 return this->read_mifare_ultralight_tag_(uid);
367 } else if (type == nfc::TAG_TYPE_UNKNOWN) {
368 ESP_LOGV(TAG, "Cannot determine tag type");
369 return make_unique<nfc::NfcTag>(uid);
370 } else {
371 return make_unique<nfc::NfcTag>(uid);
372 }
373}
374
376 this->next_task_ = READ;
377 ESP_LOGD(TAG, "Waiting to read next tag");
378}
380 this->next_task_ = CLEAN;
381 ESP_LOGD(TAG, "Waiting to clean next tag");
382}
384 this->next_task_ = FORMAT;
385 ESP_LOGD(TAG, "Waiting to format next tag");
386}
388 this->next_task_ = WRITE;
389 this->next_task_message_to_write_ = message;
390 ESP_LOGD(TAG, "Waiting to write next tag");
391}
392
393bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
394 uint8_t type = nfc::guess_tag_type(uid.size());
395 if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
396 return this->format_mifare_classic_mifare_(uid);
397 } else if (type == nfc::TAG_TYPE_2) {
398 return this->clean_mifare_ultralight_();
399 }
400 ESP_LOGE(TAG, "Unsupported Tag for formatting");
401 return false;
402}
403
404bool PN532::format_tag_(std::vector<uint8_t> &uid) {
405 uint8_t type = nfc::guess_tag_type(uid.size());
406 if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
407 return this->format_mifare_classic_ndef_(uid);
408 } else if (type == nfc::TAG_TYPE_2) {
409 return this->clean_mifare_ultralight_();
410 }
411 ESP_LOGE(TAG, "Unsupported Tag for formatting");
412 return false;
413}
414
415bool PN532::write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
416 uint8_t type = nfc::guess_tag_type(uid.size());
417 if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
418 return this->write_mifare_classic_tag_(uid, message);
419 } else if (type == nfc::TAG_TYPE_2) {
420 return this->write_mifare_ultralight_tag_(uid, message);
421 }
422 ESP_LOGE(TAG, "Unsupported Tag for formatting");
423 return false;
424}
425
427
429 ESP_LOGCONFIG(TAG, "PN532:");
430 switch (this->error_code_) {
431 case NONE:
432 break;
433 case WAKEUP_FAILED:
434 ESP_LOGE(TAG, "Wake Up command failed!");
435 break;
437 ESP_LOGE(TAG, "SAM command failed!");
438 break;
439 }
440
441 LOG_UPDATE_INTERVAL(this);
442
443 for (auto *child : this->binary_sensors_) {
444 LOG_BINARY_SENSOR(" ", "Tag", child);
445 }
446}
447
448bool PN532BinarySensor::process(std::vector<uint8_t> &data) {
449 if (data.size() != this->uid_.size())
450 return false;
451
452 for (size_t i = 0; i < data.size(); i++) {
453 if (data[i] != this->uid_[i])
454 return false;
455 }
456
457 this->publish_state(true);
458 this->found_ = true;
459 return true;
460}
461
462} // namespace pn532
463} // namespace esphome
uint8_t checksum
Definition bl0906.h:3
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message=nullptr)
void status_clear_warning()
void publish_state(bool new_state)
Publish a new state to the front-end.
std::vector< uint8_t > uid_
Definition pn532.h:133
bool process(std::vector< uint8_t > &data)
Definition pn532.cpp:448
virtual bool write_data(const std::vector< uint8_t > &data)=0
enum PN532ReadReady read_ready_(bool block)
Definition pn532.cpp:308
virtual bool is_read_ready()=0
bool format_mifare_classic_ndef_(std::vector< uint8_t > &uid)
bool format_mifare_classic_mifare_(std::vector< uint8_t > &uid)
bool write_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
Definition pn532.cpp:415
virtual bool read_response(uint8_t command, std::vector< uint8_t > &data)=0
virtual bool read_data(std::vector< uint8_t > &data, uint8_t len)=0
std::vector< PN532BinarySensor * > binary_sensors_
Definition pn532.h:98
void dump_config() override
Definition pn532.cpp:428
void setup() override
Definition pn532.cpp:17
std::unique_ptr< nfc::NfcTag > read_mifare_classic_tag_(std::vector< uint8_t > &uid)
enum esphome::pn532::PN532::NfcTask READ
std::vector< nfc::NfcOnTagTrigger * > triggers_ontagremoved_
Definition pn532.h:100
bool format_tag_(std::vector< uint8_t > &uid)
Definition pn532.cpp:404
float get_setup_priority() const override
Definition pn532.cpp:426
std::unique_ptr< nfc::NfcTag > read_tag_(std::vector< uint8_t > &uid)
Definition pn532.cpp:358
bool write_mifare_ultralight_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
CallbackManager< void()> on_finished_write_callback_
Definition pn532.h:116
void write_mode(nfc::NdefMessage *message)
Definition pn532.cpp:387
void update() override
Definition pn532.cpp:104
std::vector< nfc::NfcOnTagTrigger * > triggers_ontag_
Definition pn532.h:99
enum esphome::pn532::PN532::PN532Error NONE
bool clean_tag_(std::vector< uint8_t > &uid)
Definition pn532.cpp:393
std::unique_ptr< nfc::NfcTag > read_mifare_ultralight_tag_(std::vector< uint8_t > &uid)
bool write_mifare_classic_tag_(std::vector< uint8_t > &uid, nfc::NdefMessage *message)
bool write_command_(const std::vector< uint8_t > &data)
Definition pn532.cpp:244
nfc::NdefMessage * next_task_message_to_write_
Definition pn532.h:102
std::vector< uint8_t > current_uid_
Definition pn532.h:101
uint32_t rd_start_time_
Definition pn532.h:103
void loop() override
Definition pn532.cpp:124
uint8_t type
std::string format_uid(const std::vector< uint8_t > &uid)
Definition nfc.cpp:11
uint8_t guess_tag_type(uint8_t uid_length)
Definition nfc.cpp:15
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:50
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT yield()
Definition core.cpp:27
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28