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