ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
pn71xx.cpp
Go to the documentation of this file.
1#include <algorithm>
2#include <utility>
3
4#include "pn71xx.h"
5
7#include "esphome/core/hal.h"
9#include "esphome/core/log.h"
10
11namespace esphome::pn71xx {
12
13ESPHOME_LOG_TAG(TAG, "pn71xx");
14
15// Builds a message with a URI record and, optionally, the Home Assistant Android app record
16static std::unique_ptr<nfc::NdefMessage> build_uri_message(const std::string &uri,
17 const bool include_android_app_record) {
18 auto ndef_message = make_unique<nfc::NdefMessage>();
19
20 ndef_message->add_uri_record(uri);
21
22 if (include_android_app_record) {
23 auto ext_record = make_unique<nfc::NdefRecord>();
24 ext_record->set_tnf(nfc::TNF_EXTERNAL_TYPE);
25 ext_record->set_type(nfc::HA_TAG_ID_EXT_RECORD_TYPE);
26 ext_record->set_payload(nfc::HA_TAG_ID_EXT_RECORD_PAYLOAD);
27 ndef_message->add_record(std::move(ext_record));
28 }
29 return ndef_message;
30}
31
33 this->irq_pin_->setup();
34 this->ven_pin_->setup();
35
36 this->nci_fsm_transition_(); // kick off reset & init processes
37}
38
40 LOG_PIN(" IRQ pin: ", this->irq_pin_);
41 LOG_PIN(" VEN pin: ", this->ven_pin_);
42}
43
45 this->nci_fsm_transition_();
46 this->purge_old_tags_();
47}
48
49void PN71xx::set_tag_emulation_message(const std::shared_ptr<nfc::NdefMessage> &message) {
50 if (message == nullptr) {
51 return;
52 }
53 // encoded once here so it is validated up front and not re-encoded for every read from the reader
54 auto encoded = message->encode();
55 if (encoded.size() > CARD_EMU_T4T_MAX_NDEF_SIZE) {
56 ESP_LOGE(TAG, "Tag emulation message too long: %zu > %u bytes", encoded.size(), CARD_EMU_T4T_MAX_NDEF_SIZE);
57 return;
58 }
59 this->card_emulation_ndef_.init(encoded.size());
60 for (const uint8_t byte : encoded) {
62 }
63 ESP_LOGD(TAG, "Tag emulation message set");
64}
65
66void PN71xx::set_tag_emulation_message(const std::string &message, const bool include_android_app_record) {
67 this->set_tag_emulation_message(build_uri_message(message, include_android_app_record));
68}
69
70void PN71xx::set_tag_emulation_message(const char *message, const bool include_android_app_record) {
71 this->set_tag_emulation_message(std::string(message), include_android_app_record);
72}
73
75 if (this->listening_enabled_) {
76 this->listening_enabled_ = false;
77 this->config_refresh_pending_ = true;
78 }
79 ESP_LOGD(TAG, "Tag emulation disabled");
80}
81
83 if (this->card_emulation_ndef_.empty()) {
84 ESP_LOGE(TAG, "No NDEF message is set; tag emulation cannot be enabled");
85 return;
86 }
87 if (!this->listening_enabled_) {
88 this->listening_enabled_ = true;
89 this->config_refresh_pending_ = true;
90 }
91 ESP_LOGD(TAG, "Tag emulation enabled");
92}
93
95 if (this->polling_enabled_) {
96 this->polling_enabled_ = false;
97 this->config_refresh_pending_ = true;
98 }
99 ESP_LOGD(TAG, "Tag polling disabled");
100}
101
103 if (!this->polling_enabled_) {
104 this->polling_enabled_ = true;
105 this->config_refresh_pending_ = true;
106 }
107 ESP_LOGD(TAG, "Tag polling enabled");
108}
109
111 this->next_task_ = EP_READ;
112 ESP_LOGD(TAG, "Waiting to read next tag");
113}
114
116 this->next_task_ = EP_CLEAN;
117 ESP_LOGD(TAG, "Waiting to clean next tag");
118}
119
121 this->next_task_ = EP_FORMAT;
122 ESP_LOGD(TAG, "Waiting to format next tag");
123}
124
126 if (this->next_task_message_to_write_ == nullptr) {
127 ESP_LOGW(TAG, "Message to write must be set before setting write mode");
128 return;
129 }
130
131 this->next_task_ = EP_WRITE;
132 ESP_LOGD(TAG, "Waiting to write next tag");
133}
134
135void PN71xx::set_tag_write_message(std::shared_ptr<nfc::NdefMessage> message) {
136 this->next_task_message_to_write_ = std::move(message);
137 ESP_LOGD(TAG, "Message to write has been set");
138}
139
140void PN71xx::set_tag_write_message(const std::string &message, const bool include_android_app_record) {
141 this->set_tag_write_message(build_uri_message(message, include_android_app_record));
142}
143
144uint8_t PN71xx::set_test_mode(const TestMode test_mode, const std::vector<uint8_t> &data,
145 std::vector<uint8_t> &result) {
146 auto test_oid = TEST_PRBS_OID;
147
148 switch (test_mode) {
150 // test_oid = TEST_PRBS_OID;
151 break;
152
154 test_oid = TEST_ANTENNA_OID;
155 break;
156
158 test_oid = TEST_GET_REGISTER_OID;
159 break;
160
162 default:
163 ESP_LOGD(TAG, "Exiting test mode");
165 return nfc::STATUS_OK;
166 }
167
168 if (this->reset_core_(true, true) != nfc::STATUS_OK) {
169 ESP_LOGE(TAG, "Failed to reset NCI core");
171 result.clear();
172 return nfc::STATUS_FAILED;
173 } else {
175 }
176 if (this->init_core_() != nfc::STATUS_OK) {
177 ESP_LOGE(TAG, "Failed to initialise NCI core");
179 result.clear();
180 return nfc::STATUS_FAILED;
181 } else {
183 }
184
186 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_PROPRIETARY_GID, test_oid, data);
187
188 ESP_LOGW(TAG, "Starting test mode, OID 0x%02X", test_oid);
189 auto status = this->transceive_(tx, rx, NFCC_INIT_TIMEOUT);
190
191 if (status != nfc::STATUS_OK) {
192 ESP_LOGE(TAG, "Failed to start test mode, OID 0x%02X", test_oid);
194 result.clear();
195 } else {
196 // the payload after the status byte, if the NFCC sent one
197 const auto payload = rx.get_payload();
198 result.assign(payload.begin() + std::min<size_t>(1, payload.size()), payload.end());
199 if (!result.empty()) {
200 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
201 ESP_LOGW(TAG, "Test results: %s", nfc::format_bytes_to(buf, result));
202 }
203 }
204 return status;
205}
206
207uint8_t PN71xx::reset_core_(const bool reset_config, const bool power) {
208 this->prepare_reset();
209
210 if (power) {
211 this->ven_pin_->digital_write(true);
212 delay(NFCC_RESET_DELAY);
213 this->ven_pin_->digital_write(false);
214 delay(NFCC_RESET_DELAY);
215 this->ven_pin_->digital_write(true);
216 delay(NFCC_INIT_TIMEOUT);
217 }
218
220 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_RESET_OID,
221 {(uint8_t) reset_config});
222
223 if (this->transceive_(tx, rx, NFCC_INIT_TIMEOUT) != nfc::STATUS_OK) {
224 ESP_LOGE(TAG, "Error sending reset command");
225 return nfc::STATUS_FAILED;
226 }
227
228 if (!rx.simple_status_response_is(nfc::STATUS_OK)) {
229 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
230 ESP_LOGE(TAG, "Invalid reset response: %s", nfc::format_bytes_to(buf, rx.get_message()));
231 return rx.get_simple_status_response();
232 }
233 return this->verify_reset(rx, reset_config);
234}
235
238 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_INIT_OID);
239
240 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
241 ESP_LOGE(TAG, "Error sending initialise command");
242 return nfc::STATUS_FAILED;
243 }
244
245 if (!rx.simple_status_response_is(nfc::STATUS_OK)) {
246 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
247 ESP_LOGE(TAG, "Invalid initialise response: %s", nfc::format_bytes_to(buf, rx.get_message()));
248 return nfc::STATUS_FAILED;
249 }
250
251 return this->process_init_response(rx);
252}
253
256 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_PROPRIETARY_GID, nfc::NCI_CORE_SET_CONFIG_OID);
257
258 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
259 ESP_LOGE(TAG, "Error enabling proprietary extensions");
260 return nfc::STATUS_FAILED;
261 }
262
263 tx.set_message(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID, this->pmu_config());
264
265 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
266 ESP_LOGE(TAG, "Error sending PMU config");
267 return nfc::STATUS_FAILED;
268 }
269
270 return this->send_core_config_();
271}
272
274 std::span<const uint8_t> core_config = CORE_CONFIG_SOLO;
275 this->core_config_is_solo_ = true;
276
277 if (this->listening_enabled_ && this->polling_enabled_) {
278 core_config = CORE_CONFIG_RW_CE;
279 this->core_config_is_solo_ = false;
280 }
281
283 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::NCI_CORE_GID, nfc::NCI_CORE_SET_CONFIG_OID, core_config);
284
285 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
286 ESP_LOGW(TAG, "Error sending core config");
287 return nfc::STATUS_FAILED;
288 }
289
290 return nfc::STATUS_OK;
291}
292
294 bool core_config_should_be_solo = !(this->listening_enabled_ && this->polling_enabled_);
295
297 if (this->stop_discovery_() != nfc::STATUS_OK) {
299 return nfc::STATUS_FAILED;
300 }
302 }
303
304 if (this->core_config_is_solo_ != core_config_should_be_solo) {
305 if (this->send_core_config_() != nfc::STATUS_OK) {
306 ESP_LOGV(TAG, "Failed to refresh core config");
307 return nfc::STATUS_FAILED;
308 }
309 }
310 this->config_refresh_pending_ = false;
311 return nfc::STATUS_OK;
312}
313
316 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_MAP_OID,
317 {sizeof(RF_DISCOVER_MAP_CONFIG) / 3});
318 tx.append(RF_DISCOVER_MAP_CONFIG);
319
320 if (this->transceive_(tx, rx, NFCC_INIT_TIMEOUT) != nfc::STATUS_OK) {
321 ESP_LOGE(TAG, "Error sending discover map poll config");
322 return nfc::STATUS_FAILED;
323 }
324 return nfc::STATUS_OK;
325}
326
329 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_SET_LISTEN_MODE_ROUTING_OID,
331
332 if (this->transceive_(tx, rx, NFCC_INIT_TIMEOUT) != nfc::STATUS_OK) {
333 ESP_LOGE(TAG, "Error setting listen mode routing config");
334 return nfc::STATUS_FAILED;
335 }
336 return nfc::STATUS_OK;
337}
338
340 std::span<const uint8_t> rf_discovery_config = RF_DISCOVERY_CONFIG;
341
342 if (!this->listening_enabled_) {
343 rf_discovery_config = RF_DISCOVERY_POLL_CONFIG;
344 } else if (!this->polling_enabled_) {
345 rf_discovery_config = RF_DISCOVERY_LISTEN_CONFIG;
346 }
347
349 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_OID,
350 {static_cast<uint8_t>(rf_discovery_config.size())});
351 for (const uint8_t mode_tech : rf_discovery_config) {
352 tx.append({mode_tech, 0x01}); // RF Technology and Mode will be executed in every discovery period
353 }
354
355 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
356 switch (rx.get_simple_status_response()) {
357 // in any of these cases, we are either already in or will remain in discovery, which satisfies the function call
358 case nfc::STATUS_OK:
359 case nfc::DISCOVERY_ALREADY_STARTED:
360 case nfc::DISCOVERY_TARGET_ACTIVATION_FAILED:
361 case nfc::DISCOVERY_TEAR_DOWN:
362 return nfc::STATUS_OK;
363
364 default:
365 ESP_LOGE(TAG, "Error starting discovery");
366 return nfc::STATUS_FAILED;
367 }
368 }
369
370 return nfc::STATUS_OK;
371}
372
373uint8_t PN71xx::stop_discovery_() { return this->deactivate_(nfc::DEACTIVATION_TYPE_IDLE, NFCC_TAG_WRITE_TIMEOUT); }
374
375uint8_t PN71xx::deactivate_(const uint8_t type, const uint16_t timeout) {
377 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DEACTIVATE_OID, {type});
378
379 auto status = this->transceive_(tx, rx, timeout);
380 // if (status != nfc::STATUS_OK) {
381 // ESP_LOGE(TAG, "Error sending deactivate type %u", type);
382 // return nfc::STATUS_FAILED;
383 // }
384 return status;
385}
386
388 if (this->discovered_endpoint_.empty()) {
389 ESP_LOGW(TAG, "No cached tags to select");
390 this->stop_discovery_();
392 return;
393 }
394 this->selecting_endpoint_ = 0;
395 for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
396 if (!this->discovered_endpoint_[i].trig_called) {
397 this->selecting_endpoint_ = i;
398 break;
399 }
400 }
401 const auto &endpoint = this->discovered_endpoint_[this->selecting_endpoint_];
402 // the RF interface must match the one set for this protocol in RF_DISCOVER_MAP_CONFIG
403 uint8_t interface = nfc::INTF_FRAME;
404 if (endpoint.protocol == nfc::PROT_ISODEP) {
405 interface = nfc::INTF_ISODEP;
406 } else if (endpoint.protocol == nfc::PROT_MIFARE) {
407 interface = nfc::INTF_TAGCMD;
408 }
410 nfc::NciMessage tx(nfc::NCI_PKT_MT_CTRL_COMMAND, nfc::RF_GID, nfc::RF_DISCOVER_SELECT_OID,
411 {endpoint.id, endpoint.protocol, interface});
412
413 if (this->transceive_(tx, rx) != nfc::STATUS_OK) {
414 ESP_LOGE(TAG, "Error selecting endpoint");
415 } else {
417 }
418}
419
420uint8_t PN71xx::read_endpoint_data_(const uint8_t protocol, nfc::NfcTag &tag) {
421 switch (protocol) {
422 case nfc::PROT_MIFARE:
423 ESP_LOGV(TAG, "Reading Mifare classic");
424 return this->read_mifare_classic_tag_(tag);
425
426 case nfc::PROT_T2T:
427 ESP_LOGV(TAG, "Reading Mifare ultralight");
428 return this->read_mifare_ultralight_tag_(tag);
429
430 default:
431 ESP_LOGV(TAG, "Reading protocol 0x%02X is not supported", protocol);
432 break;
433 }
434 return nfc::STATUS_FAILED;
435}
436
437uint8_t PN71xx::clean_endpoint_(const uint8_t protocol) {
438 switch (protocol) {
439 case nfc::PROT_MIFARE:
440 return this->format_mifare_classic_mifare_();
441
442 case nfc::PROT_T2T:
443 return this->clean_mifare_ultralight_();
444
445 default:
446 ESP_LOGE(TAG, "Unsupported tag for cleaning");
447 break;
448 }
449 return nfc::STATUS_FAILED;
450}
451
452uint8_t PN71xx::format_endpoint_(const uint8_t protocol) {
453 switch (protocol) {
454 case nfc::PROT_MIFARE:
455 return this->format_mifare_classic_ndef_();
456
457 case nfc::PROT_T2T:
458 return this->clean_mifare_ultralight_();
459
460 default:
461 ESP_LOGE(TAG, "Unsupported tag for formatting");
462 break;
463 }
464 return nfc::STATUS_FAILED;
465}
466
467uint8_t PN71xx::write_endpoint_(const uint8_t protocol, nfc::NfcTagUid &uid,
468 std::shared_ptr<nfc::NdefMessage> &message) {
469 switch (protocol) {
470 case nfc::PROT_MIFARE:
471 return this->write_mifare_classic_tag_(message);
472
473 case nfc::PROT_T2T:
474 return this->write_mifare_ultralight_tag_(uid, message);
475
476 default:
477 ESP_LOGE(TAG, "Unsupported tag for writing");
478 break;
479 }
480 return nfc::STATUS_FAILED;
481}
482
483bool PN71xx::parse_uid_(const uint8_t mode_tech, const std::span<const uint8_t> rf_tech_params, nfc::NfcTagUid &uid) {
484 if (mode_tech != (nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCA)) {
485 return false;
486 }
487 // RF technology parameters: SENS_RES (2 bytes), NFCID1 length, NFCID1, ...
488 if (rf_tech_params.size() < 3) {
489 ESP_LOGE(TAG, "NFC-A parameters too short");
490 return false;
491 }
492 const uint8_t uid_length = rf_tech_params[2];
493 if (uid_length == 0 || uid_length > nfc::NFC_UID_MAX_LENGTH || rf_tech_params.size() < 3u + uid_length) {
494 ESP_LOGE(TAG, "Invalid UID length: %u", uid_length);
495 return false;
496 }
497 uid.assign(rf_tech_params.begin() + 3, rf_tech_params.begin() + 3 + uid_length);
498 return true;
499}
500
501std::unique_ptr<nfc::NfcTag> PN71xx::build_tag_(const uint8_t protocol, const nfc::NfcTagUid &uid) {
502 if (protocol == nfc::PROT_MIFARE) {
503 return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
504 }
505 if (protocol == nfc::PROT_T2T) {
506 return make_unique<nfc::NfcTag>(uid, nfc::NFC_FORUM_TYPE_2);
507 }
508 return make_unique<nfc::NfcTag>(uid);
509}
510
511size_t PN71xx::find_or_add_tag_(const uint8_t protocol, const nfc::NfcTagUid &uid) {
512 const auto tag_loc = this->find_tag_uid_(uid);
513 if (tag_loc.has_value()) {
514 ESP_LOGVV(TAG, "Tag cache updated");
515 return tag_loc.value();
516 }
517 if (this->discovered_endpoint_.size() >= MAX_DISCOVERED_ENDPOINTS) {
518 size_t oldest = 0;
519 for (size_t i = 1; i < this->discovered_endpoint_.size(); i++) {
520 if (this->discovered_endpoint_[i].last_seen < this->discovered_endpoint_[oldest].last_seen) {
521 oldest = i;
522 }
523 }
524 ESP_LOGW(TAG, "Tag cache full; dropping the tag seen longest ago");
525 this->erase_tag_(oldest);
526 }
527 this->discovered_endpoint_.emplace_next() = DiscoveredEndpoint{.last_seen = App.get_loop_component_start_time(),
528 .tag = this->build_tag_(protocol, uid),
529 .id = 0,
530 .protocol = protocol,
531 .trig_called = false};
532 ESP_LOGVV(TAG, "Tag added to cache");
533 return this->discovered_endpoint_.size() - 1;
534}
535
536optional<size_t> PN71xx::find_tag_uid_(const nfc::NfcTagUid &uid) {
537 if (!this->discovered_endpoint_.empty()) {
538 for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
539 auto existing_tag_uid = this->discovered_endpoint_[i].tag->get_uid();
540 bool uid_match = (uid.size() == existing_tag_uid.size());
541
542 if (uid_match) {
543 for (size_t i = 0; i < uid.size(); i++) {
544 uid_match &= (uid[i] == existing_tag_uid[i]);
545 }
546 if (uid_match) {
547 return i;
548 }
549 }
550 }
551 }
552 return nullopt;
553}
554
556 // millis(), not the loop start time: last_seen is stamped after tag operations that may block for seconds
557 const uint32_t now = millis();
558 for (size_t i = this->discovered_endpoint_.size(); i > 0; i--) {
559 if (now - this->discovered_endpoint_[i - 1].last_seen > this->tag_ttl_) {
560 this->erase_tag_(i - 1);
561 }
562 }
563}
564
565void PN71xx::erase_tag_(const uint8_t tag_index) {
566 if (tag_index < this->discovered_endpoint_.size()) {
567#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
568 for (auto *trigger : this->triggers_ontagremoved_) {
569 trigger->process(this->discovered_endpoint_[tag_index].tag);
570 }
571#endif
572#ifdef NFC_TAG_LISTENER_COUNT
573 for (auto *listener : this->tag_listeners_) {
574 listener->tag_off(*this->discovered_endpoint_[tag_index].tag);
575 }
576#endif
577 char uid_buf[nfc::FORMAT_UID_BUFFER_SIZE];
578 ESP_LOGI(TAG, "Tag %s removed", nfc::format_uid_to(uid_buf, this->discovered_endpoint_[tag_index].tag->get_uid()));
579 // keep the remaining entries in order; selecting_endpoint_ indexes into this list
580 for (size_t i = tag_index; i + 1 < this->discovered_endpoint_.size(); i++) {
581 this->discovered_endpoint_[i] = std::move(this->discovered_endpoint_[i + 1]);
582 }
583 // StaticVector::resize() does not destroy the dropped slot; free its tag now, not when the slot is reused
584 this->discovered_endpoint_[this->discovered_endpoint_.size() - 1].tag.reset();
585 this->discovered_endpoint_.resize(this->discovered_endpoint_.size() - 1);
586 }
587}
588
590 switch (this->nci_state_) {
592 if (this->reset_core_(true, true) != nfc::STATUS_OK) {
593 ESP_LOGE(TAG, "Failed to reset NCI core");
595 return;
596 } else {
598 }
599 [[fallthrough]];
600
602 if (this->init_core_() != nfc::STATUS_OK) {
603 ESP_LOGE(TAG, "Failed to initialise NCI core");
605 return;
606 } else {
608 }
609 [[fallthrough]];
610
612 if (this->send_init_config_() != nfc::STATUS_OK) {
613 ESP_LOGE(TAG, "Failed to send initial config");
615 return;
616 } else {
617 this->config_refresh_pending_ = false;
619 }
620 [[fallthrough]];
621
623 if (this->set_discover_map_() != nfc::STATUS_OK) {
624 ESP_LOGE(TAG, "Failed to set discover map");
626 return;
627 } else {
629 }
630 [[fallthrough]];
631
633 if (this->set_listen_mode_routing_() != nfc::STATUS_OK) {
634 ESP_LOGE(TAG, "Failed to set listen mode routing");
636 return;
637 } else {
639 }
640 [[fallthrough]];
641
644 this->stop_discovery_();
645 }
646
647 if (this->config_refresh_pending_) {
648 this->refresh_core_config_();
649 }
650
651 if (!this->listening_enabled_ && !this->polling_enabled_) {
652 return;
653 }
654
655 if (this->start_discovery_() != nfc::STATUS_OK) {
656 ESP_LOGV(TAG, "Failed to start discovery");
658 } else {
660 }
661 return;
662
665 [[fallthrough]];
666
667 // All cases below are waiting for NOTIFICATION messages
669 if (this->config_refresh_pending_) {
670 this->refresh_core_config_();
671 }
672 [[fallthrough]];
673
679 // only a notification from the NFCC ends the EP_ states; if it was lost, recover rather than wait forever.
680 // millis(), not the loop start time: the state is stamped after tag operations that may block for seconds.
682 !this->irq_pin_->digital_read() && millis() - this->last_nci_state_change_ > NFCC_STATE_TIMEOUT) {
683 ESP_LOGW(TAG, "Timed out waiting for notification in state %u; resetting NFCC", (uint8_t) this->nci_state_);
685 return;
686 }
687 if (this->irq_pin_->digital_read()) {
688 this->process_message_();
689 }
690 break;
691
692 case NCIState::TEST:
693 case NCIState::FAILED:
694 case NCIState::NONE:
695 default:
696 return;
697 }
698}
699
701 ESP_LOGVV(TAG, "nci_fsm_set_state_(%u)", (uint8_t) new_state);
702 this->nci_state_ = new_state;
704 this->error_count_ = 0;
706}
707
709 ESP_LOGVV(TAG, "nci_fsm_set_error_state_(%u); error_count_ = %u", (uint8_t) new_state, this->error_count_);
710 this->nci_state_error_ = new_state;
711 if (this->error_count_++ > NFCC_MAX_ERROR_COUNT) {
714 ESP_LOGE(TAG, "Too many initialization failures -- check device connections");
715 this->mark_failed();
717 } else {
718 ESP_LOGW(TAG, "Too many errors transitioning to state %u; resetting NFCC", (uint8_t) this->nci_state_error_);
720 }
721 }
722 return this->error_count_ > NFCC_MAX_ERROR_COUNT;
723}
724
727 if (this->read_nfcc(rx, NFCC_DEFAULT_TIMEOUT) != nfc::STATUS_OK) {
728 return; // No data
729 }
730
731 switch (rx.get_message_type()) {
732 case nfc::NCI_PKT_MT_CTRL_NOTIFICATION:
733 if (rx.get_gid() == nfc::RF_GID) {
734 switch (rx.get_oid()) {
735 case nfc::RF_INTF_ACTIVATED_OID:
736 ESP_LOGVV(TAG, "RF_INTF_ACTIVATED_OID");
738 return;
739
740 case nfc::RF_DISCOVER_OID:
741 ESP_LOGVV(TAG, "RF_DISCOVER_OID");
742 this->process_rf_discover_oid_(rx);
743 return;
744
745 case nfc::RF_DEACTIVATE_OID:
746 ESP_LOGVV(TAG, "RF_DEACTIVATE_OID: type: 0x%02X, reason: 0x%02X", rx.get_message()[3], rx.get_message()[4]);
748 return;
749
750 default:
751 ESP_LOGV(TAG, "Unimplemented RF OID received: 0x%02X", rx.get_oid());
752 }
753 } else if (rx.get_gid() == nfc::NCI_CORE_GID) {
754 switch (rx.get_oid()) {
755 case nfc::NCI_CORE_GENERIC_ERROR_OID:
756 ESP_LOGV(TAG, "NCI_CORE_GENERIC_ERROR_OID:");
757 switch (rx.get_simple_status_response()) {
758 case nfc::DISCOVERY_ALREADY_STARTED:
759 ESP_LOGV(TAG, " DISCOVERY_ALREADY_STARTED");
760 break;
761
762 case nfc::DISCOVERY_TARGET_ACTIVATION_FAILED:
763 // Tag removed too soon
764 ESP_LOGV(TAG, " DISCOVERY_TARGET_ACTIVATION_FAILED");
765 if (this->nci_state_ == NCIState::EP_SELECTING) {
767 if (!this->discovered_endpoint_.empty()) {
768 this->erase_tag_(this->selecting_endpoint_);
769 }
770 } else {
771 this->stop_discovery_();
773 }
774 break;
775
776 case nfc::DISCOVERY_TEAR_DOWN:
777 ESP_LOGV(TAG, " DISCOVERY_TEAR_DOWN");
778 break;
779
780 default:
781 ESP_LOGW(TAG, "Unknown error: 0x%02X", rx.get_simple_status_response());
782 break;
783 }
784 break;
785
786 default:
787 ESP_LOGV(TAG, "Unimplemented NCI Core OID received: 0x%02X", rx.get_oid());
788 }
789 } else {
790 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
791 ESP_LOGV(TAG, "Unimplemented notification: %s", nfc::format_bytes_to(buf, rx.get_message()));
792 }
793 break;
794
795 case nfc::NCI_PKT_MT_CTRL_RESPONSE: {
796 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
797 ESP_LOGV(TAG, "Unimplemented GID: 0x%02X OID: 0x%02X Full response: %s", rx.get_gid(), rx.get_oid(),
799 break;
800 }
801
802 case nfc::NCI_PKT_MT_CTRL_COMMAND: {
803 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
804 ESP_LOGV(TAG, "Unimplemented command: %s", nfc::format_bytes_to(buf, rx.get_message()));
805 break;
806 }
807
808 case nfc::NCI_PKT_MT_DATA:
809 this->process_data_message_(rx);
810 break;
811
812 default: {
813 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
814 ESP_LOGV(TAG, "Unimplemented message type: %s", nfc::format_bytes_to(buf, rx.get_message()));
815 break;
816 }
817 }
818}
819
820void PN71xx::process_rf_intf_activated_oid_(nfc::NciMessage &rx) { // an endpoint was activated
821 uint8_t discovery_id = rx.get_message_byte(nfc::RF_INTF_ACTIVATED_NTF_DISCOVERY_ID);
822 uint8_t interface = rx.get_message_byte(nfc::RF_INTF_ACTIVATED_NTF_INTERFACE);
823 uint8_t protocol = rx.get_message_byte(nfc::RF_INTF_ACTIVATED_NTF_PROTOCOL);
824 uint8_t mode_tech = rx.get_message_byte(nfc::RF_INTF_ACTIVATED_NTF_MODE_TECH);
825 uint8_t max_size = rx.get_message_byte(nfc::RF_INTF_ACTIVATED_NTF_MAX_SIZE);
826
827 ESP_LOGVV(TAG, "Endpoint activated -- interface: 0x%02X, protocol: 0x%02X, mode&tech: 0x%02X, max payload: %u",
828 interface, protocol, mode_tech, max_size);
829
830 if (mode_tech & nfc::MODE_LISTEN_MASK) {
831 ESP_LOGVV(TAG, "Tag activated in listen mode");
832 this->nci_fsm_set_state_(NCIState::RFST_LISTEN_ACTIVE);
833 return;
834 }
835
837 if (rx.get_message().size() < nfc::RF_INTF_ACTIVATED_NTF_RF_TECH_PARAMS) {
838 ESP_LOGE(TAG, "RF_INTF_ACTIVATED_NTF too short");
839 this->stop_discovery_();
841 return;
842 }
843 nfc::NfcTagUid uid;
844 if (!this->parse_uid_(mode_tech,
845 std::span<const uint8_t>(rx.get_message()).subspan(nfc::RF_INTF_ACTIVATED_NTF_RF_TECH_PARAMS),
846 uid)) {
847 ESP_LOGE(TAG, "Could not build tag");
848 } else {
849 auto &working_endpoint = this->discovered_endpoint_[this->find_or_add_tag_(protocol, uid)];
850 working_endpoint.id = discovery_id;
851 working_endpoint.protocol = protocol;
852 working_endpoint.last_seen = App.get_loop_component_start_time();
853
854 switch (this->next_task_) {
855 case EP_CLEAN:
856 ESP_LOGD(TAG, " Tag cleaning");
857 if (this->clean_endpoint_(working_endpoint.protocol) != nfc::STATUS_OK) {
858 ESP_LOGE(TAG, " Tag cleaning incomplete");
859 }
860 ESP_LOGD(TAG, " Tag cleaned!");
861 break;
862
863 case EP_FORMAT:
864 ESP_LOGD(TAG, " Tag formatting");
865 if (this->format_endpoint_(working_endpoint.protocol) != nfc::STATUS_OK) {
866 ESP_LOGE(TAG, "Error formatting tag as NDEF");
867 }
868 ESP_LOGD(TAG, " Tag formatted!");
869 break;
870
871 case EP_WRITE:
872 if (this->next_task_message_to_write_ != nullptr) {
873 ESP_LOGD(TAG, " Tag writing\n"
874 " Tag formatting");
875 if (this->format_endpoint_(working_endpoint.protocol) != nfc::STATUS_OK) {
876 ESP_LOGE(TAG, " Tag could not be formatted for writing");
877 } else {
878 ESP_LOGD(TAG, " Writing NDEF data");
879 if (this->write_endpoint_(working_endpoint.protocol, working_endpoint.tag->get_uid(),
880 this->next_task_message_to_write_) != nfc::STATUS_OK) {
881 ESP_LOGE(TAG, " Failed to write message to tag");
882 }
883 ESP_LOGD(TAG, " Finished writing NDEF data");
884 this->next_task_message_to_write_ = nullptr;
885 this->on_finished_write_callback_.call();
886 }
887 }
888 break;
889
890 case EP_READ:
891 default:
892 if (!working_endpoint.trig_called) {
893 char uid_buf[nfc::FORMAT_UID_BUFFER_SIZE];
894 ESP_LOGI(TAG, "Read tag type %s with UID %s", working_endpoint.tag->get_tag_type().c_str(),
895 nfc::format_uid_to(uid_buf, working_endpoint.tag->get_uid()));
896 if (this->read_endpoint_data_(working_endpoint.protocol, *working_endpoint.tag) != nfc::STATUS_OK) {
897 ESP_LOGW(TAG, " Unable to read NDEF record(s)");
898 } else if (working_endpoint.tag->has_ndef_message()) {
899 const auto &message = working_endpoint.tag->get_ndef_message();
900 const auto &records = message->get_records();
901 ESP_LOGD(TAG, " NDEF record(s):");
902 for (const auto &record : records) {
903 ESP_LOGD(TAG, " %s - %s", record->get_type().c_str(), record->get_payload().c_str());
904 }
905 } else {
906 ESP_LOGW(TAG, " No NDEF records found");
907 }
908#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
909 for (auto *trigger : this->triggers_ontag_) {
910 trigger->process(working_endpoint.tag);
911 }
912#endif
913#ifdef NFC_TAG_LISTENER_COUNT
914 for (auto *listener : this->tag_listeners_) {
915 listener->tag_on(*working_endpoint.tag);
916 }
917#endif
918 working_endpoint.trig_called = true;
919 break;
920 }
921 }
922 // the tag was present for the whole operation, which may have taken longer than tag_ttl
923 working_endpoint.last_seen = millis();
924 if (working_endpoint.protocol == nfc::PROT_MIFARE) {
926 }
927 }
928 if (this->next_task_ != EP_READ) {
929 this->read_mode();
930 }
931
932 this->stop_discovery_();
934}
935
937 if (rx.get_message().size() < nfc::RF_DISCOVER_NTF_RF_TECH_PARAMS) {
938 ESP_LOGE(TAG, "RF_DISCOVER_NTF too short");
939 return;
940 }
941 const uint8_t protocol = rx.get_message_byte(nfc::RF_DISCOVER_NTF_PROTOCOL);
942 nfc::NfcTagUid uid;
943 if (!this->parse_uid_(rx.get_message_byte(nfc::RF_DISCOVER_NTF_MODE_TECH),
944 std::span<const uint8_t>(rx.get_message()).subspan(nfc::RF_DISCOVER_NTF_RF_TECH_PARAMS), uid)) {
945 ESP_LOGE(TAG, "Could not build tag!");
946 } else {
947 auto &endpoint = this->discovered_endpoint_[this->find_or_add_tag_(protocol, uid)];
948 endpoint.id = rx.get_message_byte(nfc::RF_DISCOVER_NTF_DISCOVERY_ID);
949 endpoint.protocol = protocol;
950 endpoint.last_seen = App.get_loop_component_start_time();
951 }
952
953 const auto &ntf = rx.get_message();
954 if (ntf[ntf.size() - 1] != nfc::RF_DISCOVER_NTF_NT_MORE) {
956 ESP_LOGVV(TAG, "Discovered %zu endpoints", this->discovered_endpoint_.size());
957 }
958}
959
962
963 switch (rx.get_simple_status_response()) {
964 case nfc::DEACTIVATION_TYPE_DISCOVERY:
966 break;
967
968 case nfc::DEACTIVATION_TYPE_IDLE:
970 break;
971
972 case nfc::DEACTIVATION_TYPE_SLEEP:
973 case nfc::DEACTIVATION_TYPE_SLEEP_AF:
976 } else if (this->nci_state_ == NCIState::RFST_POLL_ACTIVE) {
978 } else {
980 }
981 break;
982
983 default:
984 break;
985 }
986}
987
989 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
990 ESP_LOGVV(TAG, "Received data message: %s", nfc::format_bytes_to(buf, rx.get_message()));
991
992 CardEmuResponse ndef_response;
993 this->card_emu_t4t_get_response_(rx.get_message(), ndef_response);
994
995 if (ndef_response.empty()) {
996 return; // no message returned, we cannot respond
997 }
998
999 nfc::NciMessage tx(nfc::NCI_PKT_MT_DATA, ndef_response);
1000 ESP_LOGVV(TAG, "Sending data message: %s", nfc::format_bytes_to(buf, tx.get_message()));
1001 if (this->transceive_(tx, rx, NFCC_DEFAULT_TIMEOUT, false) != nfc::STATUS_OK) {
1002 ESP_LOGE(TAG, "Sending reply for card emulation failed");
1003 }
1004}
1005
1006bool PN71xx::card_emu_t4t_read_ndef_(const uint16_t offset, const uint8_t length, CardEmuResponse &ndef_response) {
1007 const auto &ndef_message = this->card_emulation_ndef_;
1008 // the NDEF file is a two-byte big-endian length (NLEN) followed by the message
1009 const uint16_t ndef_msg_size = ndef_message.size();
1010 const uint32_t file_size = ndef_msg_size + 2;
1011 // the reply must also hold the two status bytes; the CC's MLe keeps well-behaved readers below this
1012 if (offset + static_cast<uint32_t>(length) > file_size ||
1013 length + sizeof(CARD_EMU_T4T_OK) > CardEmuResponse::capacity()) {
1014 return false;
1015 }
1016 for (uint32_t i = offset; i < offset + static_cast<uint32_t>(length); i++) {
1017 if (i == 0) {
1018 ndef_response.push_back(ndef_msg_size >> 8);
1019 } else if (i == 1) {
1020 ndef_response.push_back(ndef_msg_size & 0xFF);
1021 } else {
1022 ndef_response.push_back(ndef_message[i - 2]);
1023 }
1024 }
1025 if (offset + static_cast<uint32_t>(length) == file_size) {
1026 ESP_LOGD(TAG, "NDEF message sent");
1027 this->on_emulated_tag_scan_callback_.call();
1028 }
1029 return true;
1030}
1031
1032void PN71xx::card_emu_t4t_get_response_(const std::span<const uint8_t> response, CardEmuResponse &ndef_response) {
1033 ndef_response.clear();
1034 if (this->card_emulation_ndef_.empty()) {
1035 ESP_LOGE(TAG, "No NDEF message is set; tag emulation not possible");
1036 return;
1037 }
1038 if (response.size() < nfc::NCI_PKT_HEADER_SIZE) {
1039 return;
1040 }
1041
1042 const auto apdu_begin = response.begin() + nfc::NCI_PKT_HEADER_SIZE;
1043 const size_t apdu_size = response.size() - nfc::NCI_PKT_HEADER_SIZE;
1044 auto apdu_is = [&](const uint8_t *cmd, size_t cmd_size) {
1045 return apdu_size == cmd_size && std::equal(apdu_begin, response.end(), cmd);
1046 };
1047 auto apdu_starts_with = [&](const uint8_t *cmd, size_t cmd_size) {
1048 return apdu_size >= cmd_size && std::equal(cmd, cmd + cmd_size, apdu_begin);
1049 };
1050 auto append = [&](std::span<const uint8_t> bytes) {
1051 for (const uint8_t byte : bytes) {
1052 ndef_response.push_back(byte);
1053 }
1054 };
1055 bool ok = false;
1056
1057 if (apdu_is(CARD_EMU_T4T_APP_SELECT, sizeof(CARD_EMU_T4T_APP_SELECT)) ||
1058 apdu_is(CARD_EMU_T4T_APP_SELECT, sizeof(CARD_EMU_T4T_APP_SELECT) - 1)) { // Le is optional
1059 ESP_LOGVV(TAG, "CARD_EMU_NDEF_APP_SELECTED");
1061 ok = true;
1062 } else if (apdu_is(CARD_EMU_T4T_CC_SELECT, sizeof(CARD_EMU_T4T_CC_SELECT))) {
1064 ESP_LOGVV(TAG, "CARD_EMU_CC_SELECTED");
1066 ok = true;
1067 }
1068 } else if (apdu_is(CARD_EMU_T4T_NDEF_SELECT, sizeof(CARD_EMU_T4T_NDEF_SELECT))) {
1069 ESP_LOGVV(TAG, "CARD_EMU_NDEF_SELECTED");
1071 ok = true;
1072 } else if (apdu_starts_with(CARD_EMU_T4T_READ, sizeof(CARD_EMU_T4T_READ)) && apdu_size == 5) {
1073 // READ BINARY: CLA INS P1 P2 Le, where P1-P2 is the offset
1074 const uint16_t offset = (apdu_begin[2] << 8) | apdu_begin[3];
1075 const uint8_t length = apdu_begin[4];
1077 ESP_LOGVV(TAG, "CARD_EMU_T4T_READ with CARD_EMU_CC_SELECTED");
1078 if (offset + static_cast<uint32_t>(length) <= sizeof(CARD_EMU_T4T_CC)) {
1079 append(std::span<const uint8_t>(CARD_EMU_T4T_CC).subspan(offset, length));
1080 ok = true;
1081 }
1083 ESP_LOGVV(TAG, "CARD_EMU_T4T_READ with CARD_EMU_NDEF_SELECTED");
1084 ok = this->card_emu_t4t_read_ndef_(offset, length, ndef_response);
1085 }
1086 } else if (apdu_starts_with(CARD_EMU_T4T_WRITE, sizeof(CARD_EMU_T4T_WRITE)) && apdu_size >= 5) {
1087 // UPDATE BINARY: CLA INS P1 P2 Lc data
1088 const uint8_t length = apdu_begin[4];
1089 if (this->ce_state_ == CardEmulationState::CARD_EMU_NDEF_SELECTED && apdu_size >= 5u + length) {
1090 ESP_LOGVV(TAG, "CARD_EMU_T4T_WRITE");
1091 char write_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
1092 ESP_LOGD(TAG, "Received %u-byte NDEF message: %s", length,
1093 nfc::format_bytes_to(write_buf, response.subspan(nfc::NCI_PKT_HEADER_SIZE + 5, length)));
1094 ok = true;
1095 }
1096 }
1097
1098 if (ok) {
1099 append(CARD_EMU_T4T_OK);
1100 } else {
1101 ndef_response.clear();
1102 append(CARD_EMU_T4T_NOK);
1104 }
1105}
1106
1107uint8_t PN71xx::transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, const uint16_t timeout,
1108 const bool expect_notification) {
1109 char buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
1110
1111 // The NFCC may refuse a frame while it wakes from standby; the whole frame must then be sent again.
1112 // A command must never be re-sent once it was accepted, as the NFCC will answer it (NCI 1.0, 3.2.1).
1113 uint8_t attempts = 0;
1114 while (this->write_nfcc(tx) != nfc::STATUS_OK) {
1115 if (++attempts >= NFCC_MAX_COMM_FAILS) {
1116 ESP_LOGE(TAG, "Error sending message");
1117 return nfc::STATUS_FAILED;
1118 }
1119 delay(NFCC_WRITE_RETRY_DELAY);
1120 }
1121 ESP_LOGVV(TAG, "Wrote: %s", nfc::format_bytes_to(buf, tx.get_message()));
1122
1123 if (!tx.message_type_is(nfc::NCI_PKT_MT_DATA)) {
1124 // Notifications may already be queued ahead of the response; skip them. They carry the same GID and OID
1125 // as some responses (e.g. RF_DEACTIVATE_NTF), so the message type must be checked. A response to an earlier
1126 // command whose read timed out (a tag leaving the field delays RF_DEACTIVATE_RSP) is skipped the same way.
1127 for (uint8_t i = 0; i < NFCC_MAX_COMM_FAILS; i++) {
1128 if (this->read_nfcc(rx, timeout) != nfc::STATUS_OK) {
1129 ESP_LOGW(TAG, "Error receiving response");
1130 return nfc::STATUS_FAILED;
1131 }
1132 ESP_LOGVV(TAG, "Read: %s", nfc::format_bytes_to(buf, rx.get_message()));
1133 if (rx.message_type_is(nfc::NCI_PKT_MT_CTRL_RESPONSE) && rx.get_gid() == tx.get_gid() &&
1134 rx.get_oid() == tx.get_oid()) {
1135 break;
1136 }
1137 ESP_LOGW(TAG, "Discarding message received while waiting for response: %s",
1139 }
1140 // for commands, the GID and OID should match and the status should be OK
1141 if (!rx.message_type_is(nfc::NCI_PKT_MT_CTRL_RESPONSE) || (rx.get_gid() != tx.get_gid()) ||
1142 (rx.get_oid() != tx.get_oid())) {
1143 ESP_LOGE(TAG, "Incorrect response to command: %s", nfc::format_bytes_to(buf, rx.get_message()));
1144 return nfc::STATUS_FAILED;
1145 }
1146
1147 if (!rx.simple_status_response_is(nfc::STATUS_OK)) {
1148 ESP_LOGE(TAG, "Error in response to command: %s", nfc::format_bytes_to(buf, rx.get_message()));
1149 }
1150 return rx.get_simple_status_response();
1151 }
1152
1153 // when sending data to the endpoint, the first message is the credit notification from the NFCC
1154 if (this->read_nfcc(rx, timeout) != nfc::STATUS_OK) {
1155 ESP_LOGW(TAG, "Error receiving credit notification");
1156 return nfc::STATUS_FAILED;
1157 }
1158 ESP_LOGVV(TAG, "Read: %s", nfc::format_bytes_to(buf, rx.get_message()));
1159 if ((!rx.message_type_is(nfc::NCI_PKT_MT_CTRL_NOTIFICATION)) || (!rx.gid_is(nfc::NCI_CORE_GID)) ||
1160 (!rx.oid_is(nfc::NCI_CORE_CONN_CREDITS_OID)) || (!rx.message_length_is(3))) {
1161 ESP_LOGE(TAG, "Incorrect response to data message: %s", nfc::format_bytes_to(buf, rx.get_message()));
1162 return nfc::STATUS_FAILED;
1163 }
1164
1165 if (expect_notification) {
1166 // the endpoint's answer follows in a data message
1167 if (this->read_nfcc(rx, timeout) != nfc::STATUS_OK) {
1168 ESP_LOGE(TAG, "Error receiving data from endpoint");
1169 return nfc::STATUS_FAILED;
1170 }
1171 ESP_LOGVV(TAG, "Read: %s", nfc::format_bytes_to(buf, rx.get_message()));
1172 }
1173
1174 return nfc::STATUS_OK;
1175}
1176
1177uint8_t PN71xx::wait_for_irq_(uint16_t timeout, bool pin_state) {
1178 auto start_time = millis();
1179
1180 while (millis() - start_time < timeout) {
1181 if (this->irq_pin_->digital_read() == pin_state) {
1182 return nfc::STATUS_OK;
1183 }
1184 }
1185 return nfc::STATUS_FAILED;
1186}
1187
1188} // namespace esphome::pn71xx
uint8_t status
Definition bl0942.h:8
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
bool empty() const
Definition helpers.h:793
size_t size() const
Definition helpers.h:792
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:743
void init(size_t n)
Definition helpers.h:706
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
size_t size() const
Definition helpers.h:355
void push_back(const T &value)
Definition helpers.h:325
bool empty() const
Definition helpers.h:357
static constexpr size_t capacity()
Definition helpers.h:356
void assign(InputIt first, InputIt last)
Definition helpers.h:338
One NCI packet, held in a fixed buffer so building and reading messages never allocates.
Definition nci_message.h:16
bool message_type_is(uint8_t message_type) const
uint8_t get_oid() const
bool simple_status_response_is(uint8_t response) const
uint8_t get_message_byte(uint8_t offset) const
bool gid_is(uint8_t gid) const
bool message_length_is(uint8_t message_length, bool recompute=false)
std::span< const uint8_t > get_payload() const
The payload bytes that follow the header.
void set_message(uint8_t message_type, std::span< const uint8_t > payload)
uint8_t get_gid() const
uint8_t get_simple_status_response() const
bool oid_is(uint8_t oid) const
uint8_t get_message_type() const
void append(std::span< const uint8_t > data)
Appends bytes to the payload; bytes that do not fit are dropped.
StaticVector< NfcTagListener *, NFC_TAG_LISTENER_COUNT > tag_listeners_
Definition nfc.h:90
void loop() override
Definition pn71xx.cpp:44
uint8_t write_mifare_classic_tag_(const std::shared_ptr< nfc::NdefMessage > &message)
StaticVector< DiscoveredEndpoint, MAX_DISCOVERED_ENDPOINTS > discovered_endpoint_
Definition pn71xx.h:301
uint8_t set_test_mode(TestMode test_mode, const std::vector< uint8_t > &data, std::vector< uint8_t > &result)
Definition pn71xx.cpp:144
StaticVector< nfc::NfcOnTagTrigger *, PN71XX_ON_TAG_TRIGGER_COUNT > triggers_ontag_
Definition pn71xx.h:304
uint8_t wait_for_irq_(uint16_t timeout=NFCC_DEFAULT_TIMEOUT, bool pin_state=true)
Definition pn71xx.cpp:1177
virtual void prepare_reset()
Chip hooks Called before the NFCC is reset, e.g.
Definition pn71xx.h:209
uint32_t last_nci_state_change_
Definition pn71xx.h:314
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr< nfc::NdefMessage > &message)
uint8_t send_core_config_()
Definition pn71xx.cpp:273
uint8_t stop_discovery_()
Definition pn71xx.cpp:373
void process_message_()
parse & process incoming messages from the NFCC
Definition pn71xx.cpp:725
StaticVector< nfc::NfcOnTagTrigger *, PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT > triggers_ontagremoved_
Definition pn71xx.h:307
size_t find_or_add_tag_(uint8_t protocol, const nfc::NfcTagUid &uid)
Finds a cached endpoint by UID, or caches a new one, evicting the entry seen longest ago if the cache...
Definition pn71xx.cpp:511
uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout=NFCC_DEFAULT_TIMEOUT, bool expect_notification=true)
Definition pn71xx.cpp:1107
std::unique_ptr< nfc::NfcTag > build_tag_(uint8_t protocol, const nfc::NfcTagUid &uid)
Definition pn71xx.cpp:501
uint8_t deactivate_(uint8_t type, uint16_t timeout=NFCC_DEFAULT_TIMEOUT)
Definition pn71xx.cpp:375
void set_tag_emulation_message(const std::shared_ptr< nfc::NdefMessage > &message)
Definition pn71xx.cpp:49
uint8_t set_discover_map_()
Definition pn71xx.cpp:314
void nci_fsm_set_state_(NCIState new_state)
set new controller state
Definition pn71xx.cpp:700
void nci_fsm_transition_()
advance controller state as required
Definition pn71xx.cpp:589
optional< size_t > find_tag_uid_(const nfc::NfcTagUid &uid)
Definition pn71xx.cpp:536
uint8_t send_init_config_()
Definition pn71xx.cpp:254
uint8_t refresh_core_config_()
Definition pn71xx.cpp:293
uint8_t read_mifare_classic_tag_(nfc::NfcTag &tag)
bool card_emu_t4t_read_ndef_(uint16_t offset, uint8_t length, CardEmuResponse &ndef_response)
Definition pn71xx.cpp:1006
uint8_t format_endpoint_(uint8_t protocol)
Definition pn71xx.cpp:452
void process_rf_deactivate_oid_(nfc::NciMessage &rx)
Definition pn71xx.cpp:960
virtual uint8_t verify_reset(nfc::NciMessage &rx, bool reset_config)=0
Validates what follows a successful CORE_RESET_RSP (in rx); may read more messages from the NFCC.
uint8_t read_endpoint_data_(uint8_t protocol, nfc::NfcTag &tag)
Definition pn71xx.cpp:420
virtual std::span< const uint8_t > listen_mode_routing_config() const =0
Payload of the RF_SET_LISTEN_MODE_ROUTING_CMD.
void set_tag_write_message(std::shared_ptr< nfc::NdefMessage > message)
Definition pn71xx.cpp:135
uint8_t clean_endpoint_(uint8_t protocol)
Definition pn71xx.cpp:437
void process_data_message_(nfc::NciMessage &rx)
Definition pn71xx.cpp:988
std::shared_ptr< nfc::NdefMessage > next_task_message_to_write_
Definition pn71xx.h:309
LazyCallbackManager< void()> on_emulated_tag_scan_callback_
Definition pn71xx.h:298
virtual uint8_t process_init_response(nfc::NciMessage &rx)=0
Logs chip information from a successful CORE_INIT_RSP.
virtual std::span< const uint8_t > pmu_config() const =0
Parameters for the CORE_SET_CONFIG_CMD that configures the power management unit.
void process_rf_discover_oid_(nfc::NciMessage &rx)
Definition pn71xx.cpp:936
uint8_t reset_core_(bool reset_config, bool power)
Definition pn71xx.cpp:207
uint8_t write_endpoint_(uint8_t protocol, nfc::NfcTagUid &uid, std::shared_ptr< nfc::NdefMessage > &message)
Definition pn71xx.cpp:467
uint8_t set_listen_mode_routing_()
Definition pn71xx.cpp:327
void dump_config() override
Definition pn71xx.cpp:39
LazyCallbackManager< void()> on_finished_write_callback_
Definition pn71xx.h:299
uint8_t read_mifare_ultralight_tag_(nfc::NfcTag &tag)
void process_rf_intf_activated_oid_(nfc::NciMessage &rx)
Definition pn71xx.cpp:820
FixedVector< uint8_t > card_emulation_ndef_
Definition pn71xx.h:302
virtual uint8_t write_nfcc(nfc::NciMessage &tx)=0
bool nci_fsm_set_error_state_(NCIState new_state)
setting controller to this state caused an error; returns true if too many errors/failures
Definition pn71xx.cpp:708
uint8_t selecting_endpoint_
Definition pn71xx.h:322
virtual uint8_t read_nfcc(nfc::NciMessage &rx, uint16_t timeout)=0
CardEmulationState ce_state_
Definition pn71xx.h:318
void card_emu_t4t_get_response_(std::span< const uint8_t > response, CardEmuResponse &ndef_response)
Definition pn71xx.cpp:1032
uint8_t start_discovery_()
Definition pn71xx.cpp:339
void setup() override
Definition pn71xx.cpp:32
void erase_tag_(uint8_t tag_index)
Definition pn71xx.cpp:565
bool parse_uid_(uint8_t mode_tech, std::span< const uint8_t > rf_tech_params, nfc::NfcTagUid &uid)
Reads the UID from the RF technology parameters of a discovery or activation notification.
Definition pn71xx.cpp:483
NCIState nci_state_error_
Definition pn71xx.h:320
const LogString * message
Definition component.cpp:35
uint8_t type
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:14
char * format_uid_to(char *buffer, std::span< const uint8_t > uid)
Format UID to buffer with '-' separator (e.g., "04-11-22-33"). Returns buffer for inline use.
Definition nfc.cpp:10
ESPHOME_LOG_TAG(TAG, "pn71xx")
if(written< 0)
Definition helpers.h:1188
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
uint16_t length
Definition tt21100.cpp:0