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