ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
pn71xx.h
Go to the documentation of this file.
1#pragma once
2
9#include "esphome/core/gpio.h"
11
12#include <array>
13#include <functional>
14#include <span>
15
16namespace esphome::pn71xx {
17
18// Time to wait for the NFCC to answer. NXP's reference stack waits 1 s for a response; 10 ms was short enough
19// that a slow RF_DEACTIVATE_RSP, for example when a tag leaves the field, caused a full NFCC reset.
20static constexpr uint16_t NFCC_DEFAULT_TIMEOUT = 100;
21static constexpr uint16_t NFCC_INIT_TIMEOUT = 50;
22static constexpr uint16_t NFCC_TAG_WRITE_TIMEOUT = 100;
23// Time to wait for IRQ to drop after a read; it drops within microseconds unless another message is queued
24static constexpr uint16_t NFCC_IRQ_CLEAR_TIMEOUT = 5;
25// Length of the VEN and DWL_REQ pulses when resetting the NFCC
26static constexpr uint16_t NFCC_RESET_DELAY = 10;
27// Time to wait before resending a frame the NFCC refused, e.g. while waking from standby
28static constexpr uint16_t NFCC_WRITE_RETRY_DELAY = 5;
29// Longest time the FSM may wait for a notification that ends a transitional state before resetting the NFCC
30static constexpr uint32_t NFCC_STATE_TIMEOUT = 1000;
31
32static constexpr uint8_t NFCC_MAX_COMM_FAILS = 3;
33static constexpr uint8_t NFCC_MAX_ERROR_COUNT = 10;
34
35static constexpr uint8_t XCHG_DATA_OID = 0x10;
36static constexpr uint8_t MF_SECTORSEL_OID = 0x32;
37static constexpr uint8_t MFC_AUTHENTICATE_OID = 0x40;
38static constexpr uint8_t TEST_PRBS_OID = 0x30;
39static constexpr uint8_t TEST_ANTENNA_OID = 0x3D;
40static constexpr uint8_t TEST_GET_REGISTER_OID = 0x33;
41
42static constexpr uint8_t MFC_AUTHENTICATE_PARAM_KS_A = 0x00; // key select A
43static constexpr uint8_t MFC_AUTHENTICATE_PARAM_KS_B = 0x80; // key select B
44static constexpr uint8_t MFC_AUTHENTICATE_PARAM_EMBED_KEY = 0x10;
45
46static constexpr uint8_t CARD_EMU_T4T_APP_SELECT[] = {0x00, 0xA4, 0x04, 0x00, 0x07, 0xD2, 0x76,
47 0x00, 0x00, 0x85, 0x01, 0x01, 0x00};
48// MLe is 0xFD so a full-length READ BINARY response plus the two status bytes fits in one NCI data packet
49static constexpr uint8_t CARD_EMU_T4T_CC[] = {0x00, 0x0F, 0x20, 0x00, 0xFD, 0x00, 0xFF, 0x04,
50 0x06, 0xE1, 0x04, 0x00, 0xFF, 0x00, 0x00};
51// Largest NDEF message that fits in the emulated NDEF file (max file size in the CC, less the 2-byte length)
52static constexpr uint16_t CARD_EMU_T4T_MAX_NDEF_SIZE = 0xFF - 2;
53static constexpr uint8_t CARD_EMU_T4T_CC_SELECT[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x03};
54static constexpr uint8_t CARD_EMU_T4T_NDEF_SELECT[] = {0x00, 0xA4, 0x00, 0x0C, 0x02, 0xE1, 0x04};
55static constexpr uint8_t CARD_EMU_T4T_READ[] = {0x00, 0xB0};
56static constexpr uint8_t CARD_EMU_T4T_WRITE[] = {0x00, 0xD6};
57static constexpr uint8_t CARD_EMU_T4T_OK[] = {0x90, 0x00};
58static constexpr uint8_t CARD_EMU_T4T_NOK[] = {0x6A, 0x82};
59
60static constexpr uint8_t CORE_CONFIG_SOLO[] = {0x01, // Number of parameter fields
61 0x00, // config param identifier (TOTAL_DURATION)
62 0x02, // length of value
63 0x01, // TOTAL_DURATION (low)...
64 0x00}; // TOTAL_DURATION (high): 1 ms
65
66static constexpr uint8_t CORE_CONFIG_RW_CE[] = {0x01, // Number of parameter fields
67 0x00, // config param identifier (TOTAL_DURATION)
68 0x02, // length of value
69 0xF8, // TOTAL_DURATION (low)...
70 0x02}; // TOTAL_DURATION (high): 760 ms
71
72static constexpr uint8_t RF_DISCOVER_MAP_CONFIG[] = {
73 // poll modes
74 nfc::PROT_T1T, nfc::RF_DISCOVER_MAP_MODE_POLL,
75 nfc::INTF_FRAME, // poll mode
76 nfc::PROT_T2T, nfc::RF_DISCOVER_MAP_MODE_POLL,
77 nfc::INTF_FRAME, // poll mode
78 nfc::PROT_T3T, nfc::RF_DISCOVER_MAP_MODE_POLL,
79 nfc::INTF_FRAME, // poll mode
80 nfc::PROT_ISODEP, nfc::RF_DISCOVER_MAP_MODE_POLL | nfc::RF_DISCOVER_MAP_MODE_LISTEN,
81 nfc::INTF_ISODEP, // poll & listen mode
82 nfc::PROT_MIFARE, nfc::RF_DISCOVER_MAP_MODE_POLL,
83 nfc::INTF_TAGCMD}; // poll mode
84
85static constexpr uint8_t RF_DISCOVERY_LISTEN_CONFIG[] = {
86 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCA, // listen mode
87 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCB, // listen mode
88 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCF}; // listen mode
89
90static constexpr uint8_t RF_DISCOVERY_POLL_CONFIG[] = {nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCA, // poll mode
91 nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCB, // poll mode
92 nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCF}; // poll mode
93
94static constexpr uint8_t RF_DISCOVERY_CONFIG[] = {nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCA, // poll mode
95 nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCB, // poll mode
96 nfc::MODE_POLL | nfc::TECH_PASSIVE_NFCF, // poll mode
97 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCA, // listen mode
98 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCB, // listen mode
99 nfc::MODE_LISTEN_MASK | nfc::TECH_PASSIVE_NFCF}; // listen mode
100
108
128
129enum class TestMode : uint8_t {
130 TEST_NONE = 0x00,
131 TEST_PRBS,
134};
135
141static constexpr uint32_t MIFARE_CLASSIC_MAX_NDEF_SIZE = 3440;
144static constexpr size_t MAX_DISCOVERED_ENDPOINTS = 8;
145
148 std::unique_ptr<nfc::NfcTag> tag;
149 uint8_t id;
150 uint8_t protocol;
152};
153
156class PN71xx : public nfc::Nfcc, public Component {
157 public:
158 void setup() override;
159 void dump_config() override;
160 void loop() override;
161
162 void set_irq_pin(GPIOPin *irq_pin) { this->irq_pin_ = irq_pin; }
163 void set_ven_pin(GPIOPin *ven_pin) { this->ven_pin_ = ven_pin; }
164
166 void set_tag_emulation_message(const std::shared_ptr<nfc::NdefMessage> &message);
167 void set_tag_emulation_message(const std::string &message, bool include_android_app_record = true);
168 void set_tag_emulation_message(const char *message, bool include_android_app_record = true);
172
173 void set_polling_off();
174 void set_polling_on();
175 bool polling_enabled() { return this->polling_enabled_; }
176
177#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
178 void register_ontag_trigger(nfc::NfcOnTagTrigger *trig) { this->triggers_ontag_.push_back(trig); }
179#endif
180#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
182#endif
183
184 template<typename F> void add_on_emulated_tag_scan_callback(F &&callback) {
185 this->on_emulated_tag_scan_callback_.add(std::forward<F>(callback));
186 }
187
188 template<typename F> void add_on_finished_write_callback(F &&callback) {
189 this->on_finished_write_callback_.add(std::forward<F>(callback));
190 }
191
192 bool is_writing() { return this->next_task_ != EP_READ; };
193
194 void read_mode();
195 void clean_mode();
196 void format_mode();
197 void write_mode();
198 void set_tag_write_message(std::shared_ptr<nfc::NdefMessage> message);
199 void set_tag_write_message(const std::string &message, bool include_android_app_record = true);
200
201 uint8_t set_test_mode(TestMode test_mode, const std::vector<uint8_t> &data, std::vector<uint8_t> &result);
202
203 protected:
204 uint8_t reset_core_(bool reset_config, bool power);
205 uint8_t init_core_();
206
209 virtual void prepare_reset() {}
211 virtual uint8_t verify_reset(nfc::NciMessage &rx, bool reset_config) = 0;
213 virtual uint8_t process_init_response(nfc::NciMessage &rx) = 0;
215 virtual std::span<const uint8_t> pmu_config() const = 0;
217 virtual std::span<const uint8_t> listen_mode_routing_config() const = 0;
218
219 uint8_t send_init_config_();
220 uint8_t send_core_config_();
221 uint8_t refresh_core_config_();
222
223 uint8_t set_discover_map_();
224
225 uint8_t set_listen_mode_routing_();
226
227 uint8_t start_discovery_();
228 uint8_t stop_discovery_();
229 uint8_t deactivate_(uint8_t type, uint16_t timeout = NFCC_DEFAULT_TIMEOUT);
230
231 void select_endpoint_();
232
233 uint8_t read_endpoint_data_(uint8_t protocol, nfc::NfcTag &tag);
234 uint8_t clean_endpoint_(uint8_t protocol);
235 uint8_t format_endpoint_(uint8_t protocol);
236 uint8_t write_endpoint_(uint8_t protocol, nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
237
239 bool parse_uid_(uint8_t mode_tech, std::span<const uint8_t> rf_tech_params, nfc::NfcTagUid &uid);
240 std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t protocol, const nfc::NfcTagUid &uid);
242 size_t find_or_add_tag_(uint8_t protocol, const nfc::NfcTagUid &uid);
243 optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
244 void purge_old_tags_();
245 void erase_tag_(uint8_t tag_index);
246
248 void nci_fsm_transition_();
250 void nci_fsm_set_state_(NCIState new_state);
252 bool nci_fsm_set_error_state_(NCIState new_state);
254 void process_message_();
259
260 void card_emu_t4t_get_response_(std::span<const uint8_t> response, CardEmuResponse &ndef_response);
261 bool card_emu_t4t_read_ndef_(uint16_t offset, uint8_t length, CardEmuResponse &ndef_response);
262
263 uint8_t transceive_(nfc::NciMessage &tx, nfc::NciMessage &rx, uint16_t timeout = NFCC_DEFAULT_TIMEOUT,
264 bool expect_notification = true);
265 virtual uint8_t read_nfcc(nfc::NciMessage &rx, uint16_t timeout) = 0;
266 virtual uint8_t write_nfcc(nfc::NciMessage &tx) = 0;
267
268 uint8_t wait_for_irq_(uint16_t timeout = NFCC_DEFAULT_TIMEOUT, bool pin_state = true);
269
271 uint8_t read_mifare_classic_block_(uint8_t block_num, std::array<uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE> &data);
272 uint8_t write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len);
273 uint8_t auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key);
274 uint8_t sect_to_auth_(uint8_t block_num);
277 uint8_t write_mifare_classic_tag_(const std::shared_ptr<nfc::NdefMessage> &message);
278 uint8_t halt_mifare_classic_tag_();
279
281 uint8_t read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, UltralightReadBuffer &data);
282 bool is_mifare_ultralight_formatted_(std::span<const uint8_t> page_3_to_6);
284 uint8_t find_mifare_ultralight_ndef_(std::span<const uint8_t> page_3_to_6, uint8_t &message_length,
285 uint8_t &message_start_index);
286 uint8_t write_mifare_ultralight_page_(uint8_t page_num, const uint8_t *write_data, size_t len);
287 uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
288 uint8_t clean_mifare_ultralight_();
289
296
297 // members are ordered by alignment, widest first, to minimize padding
300
302 FixedVector<uint8_t> card_emulation_ndef_; // encoded emulation message; empty when none is set
303#ifdef PN71XX_ON_TAG_TRIGGER_COUNT
305#endif
306#ifdef PN71XX_ON_TAG_REMOVED_TRIGGER_COUNT
308#endif
309 std::shared_ptr<nfc::NdefMessage> next_task_message_to_write_;
310
311 GPIOPin *irq_pin_{nullptr};
312 GPIOPin *ven_pin_{nullptr};
313
316
321 uint8_t error_count_{0};
323
328};
329
330} // namespace esphome::pn71xx
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:616
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:286
One NCI packet, held in a fixed buffer so building and reading messages never allocates.
Definition nci_message.h:16
Fires with the formatted UID and the tag itself; the tag is passed by reference so no copy is made pe...
Definition automation.h:11
Common driver for the NXP PN71xx family of NCI NFC controllers.
Definition pn71xx.h:156
void loop() override
Definition pn71xx.cpp:44
uint8_t write_mifare_classic_tag_(const std::shared_ptr< nfc::NdefMessage > &message)
void set_ven_pin(GPIOPin *ven_pin)
Definition pn71xx.h:163
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
bool tag_emulation_enabled()
Definition pn71xx.h:171
uint8_t sect_to_auth_(uint8_t block_num)
uint8_t find_mifare_ultralight_ndef_(std::span< const uint8_t > page_3_to_6, uint8_t &message_length, uint8_t &message_start_index)
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
uint8_t auth_mifare_classic_block_(uint8_t block_num, uint8_t key_num, const uint8_t *key)
void add_on_emulated_tag_scan_callback(F &&callback)
Definition pn71xx.h:184
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
void register_ontagremoved_trigger(nfc::NfcOnTagTrigger *trig)
Definition pn71xx.h:181
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 set_irq_pin(GPIOPin *irq_pin)
Definition pn71xx.h:162
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_mifare_classic_block_(uint8_t block_num, std::array< uint8_t, nfc::MIFARE_CLASSIC_BLOCK_SIZE > &data)
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.
bool is_mifare_ultralight_formatted_(std::span< const uint8_t > page_3_to_6)
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
uint8_t read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, UltralightReadBuffer &data)
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
void register_ontag_trigger(nfc::NfcOnTagTrigger *trig)
Definition pn71xx.h:178
uint8_t set_listen_mode_routing_()
Definition pn71xx.cpp:327
void dump_config() override
Definition pn71xx.cpp:39
uint8_t write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len)
LazyCallbackManager< void()> on_finished_write_callback_
Definition pn71xx.h:299
void add_on_finished_write_callback(F &&callback)
Definition pn71xx.h:188
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
void set_tag_ttl(uint32_t ttl)
Definition pn71xx.h:165
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
uint8_t write_mifare_ultralight_page_(uint8_t page_num, const uint8_t *write_data, size_t len)
NCIState nci_state_error_
Definition pn71xx.h:320
const LogString * message
Definition component.cpp:35
uint32_t ttl
uint8_t type
const void size_t len
Definition hal.h:68
static void uint32_t
std::unique_ptr< nfc::NfcTag > tag
Definition pn71xx.h:148