ESPHome  2024.4.1
pn532_spi.cpp
Go to the documentation of this file.
1 #include "pn532_spi.h"
2 #include "esphome/core/log.h"
3 
4 // Based on:
5 // - https://cdn-shop.adafruit.com/datasheets/PN532C106_Application+Note_v1.2.pdf
6 // - https://www.nxp.com/docs/en/nxp/application-notes/AN133910.pdf
7 // - https://www.nxp.com/docs/en/nxp/application-notes/153710.pdf
8 
9 namespace esphome {
10 namespace pn532_spi {
11 
12 static const char *const TAG = "pn532_spi";
13 
15  ESP_LOGI(TAG, "PN532Spi setup started!");
16  this->spi_setup();
17 
18  this->cs_->digital_write(false);
19  delay(10);
20  ESP_LOGI(TAG, "SPI setup finished!");
21  PN532::setup();
22 }
23 
25  this->enable();
26  this->write_byte(0x02);
27  bool ready = this->read_byte() == 0x01;
28  this->disable();
29  return ready;
30 }
31 
32 bool PN532Spi::write_data(const std::vector<uint8_t> &data) {
33  this->enable();
34  delay(2);
35  // First byte, communication mode: Write data
36  this->write_byte(0x01);
37  ESP_LOGV(TAG, "Writing data: %s", format_hex_pretty(data).c_str());
38  this->write_array(data.data(), data.size());
39  this->disable();
40 
41  return true;
42 }
43 
44 bool PN532Spi::read_data(std::vector<uint8_t> &data, uint8_t len) {
45  if (this->read_ready_(true) != pn532::PN532ReadReady::READY) {
46  return false;
47  }
48 
49  // Read data (transmission from the PN532 to the host)
50  this->enable();
51  delay(2);
52  this->write_byte(0x03);
53 
54  ESP_LOGV(TAG, "Reading data...");
55 
56  data.resize(len);
57  this->read_array(data.data(), len);
58  this->disable();
59  data.insert(data.begin(), 0x01);
60  ESP_LOGV(TAG, "Read data: %s", format_hex_pretty(data).c_str());
61  return true;
62 }
63 
64 bool PN532Spi::read_response(uint8_t command, std::vector<uint8_t> &data) {
65  ESP_LOGV(TAG, "Reading response");
66 
67  if (this->read_ready_(true) != pn532::PN532ReadReady::READY) {
68  return false;
69  }
70 
71  this->enable();
72  delay(2);
73  this->write_byte(0x03);
74 
75  std::vector<uint8_t> header(7);
76  this->read_array(header.data(), 7);
77 
78  ESP_LOGV(TAG, "Header data: %s", format_hex_pretty(header).c_str());
79 
80  if (header[0] != 0x00 && header[1] != 0x00 && header[2] != 0xFF) {
81  // invalid packet
82  ESP_LOGV(TAG, "read data invalid preamble!");
83  return false;
84  }
85 
86  bool valid_header = (static_cast<uint8_t>(header[3] + header[4]) == 0 && // LCS, len + lcs = 0
87  header[5] == 0xD5 && // TFI - frame from PN532 to system controller
88  header[6] == command + 1); // Correct command response
89 
90  if (!valid_header) {
91  ESP_LOGV(TAG, "read data invalid header!");
92  return false;
93  }
94 
95  // full length of message, including command response
96  uint8_t full_len = header[3];
97  // length of data, excluding command response
98  uint8_t len = full_len - 1;
99  if (full_len == 0)
100  len = 0;
101 
102  ESP_LOGV(TAG, "Reading response of length %d", len);
103 
104  data.resize(len + 1);
105  this->read_array(data.data(), len + 1);
106  this->disable();
107 
108  ESP_LOGV(TAG, "Response data: %s", format_hex_pretty(data).c_str());
109 
110  uint8_t checksum = header[5] + header[6]; // TFI + Command response code
111  for (int i = 0; i < len - 1; i++) {
112  uint8_t dat = data[i];
113  checksum += dat;
114  }
115  checksum = ~checksum + 1;
116 
117  if (data[len - 1] != checksum) {
118  ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len - 1], checksum);
119  return false;
120  }
121 
122  if (data[len] != 0x00) {
123  ESP_LOGV(TAG, "read data invalid postamble!");
124  return false;
125  }
126 
127  data.erase(data.end() - 2, data.end()); // Remove checksum and postamble
128 
129  return true;
130 }
131 
133  PN532::dump_config();
134  LOG_PIN(" CS Pin: ", this->cs_);
135 }
136 
137 } // namespace pn532_spi
138 } // namespace esphome
void setup()
virtual void digital_write(bool value)=0
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:361
void dump_config() override
Definition: pn532_spi.cpp:132
bool read_response(uint8_t command, std::vector< uint8_t > &data) override
Definition: pn532_spi.cpp:64
GPIOPin * cs_
Definition: spi.h:395
bool read_data(std::vector< uint8_t > &data, uint8_t len) override
Definition: pn532_spi.cpp:44
enum PN532ReadReady read_ready_(bool block)
Definition: pn532.cpp:310
bool write_data(const std::vector< uint8_t > &data) override
Definition: pn532_spi.cpp:32
uint8_t checksum
Definition: bl0939.h:35
std::string size_t len
Definition: helpers.h:292
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
bool is_read_ready() override
Definition: pn532_spi.cpp:24
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26