mirror of
https://github.com/esphome/esphome.git
synced 2026-09-30 02:22:57 +08:00
Merge branch 'esp8266-native-pch' into esp32-idf-pch
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,28 @@ static const uint8_t PN532_COMMAND_INDATAEXCHANGE = 0x40;
|
||||
static const uint8_t PN532_COMMAND_INLISTPASSIVETARGET = 0x4A;
|
||||
static const uint8_t PN532_COMMAND_POWERDOWN = 0x16;
|
||||
|
||||
enum PN532ReadReady {
|
||||
enum PN532ReadReady : uint8_t {
|
||||
WOULDBLOCK = 0,
|
||||
TIMEOUT,
|
||||
READY,
|
||||
};
|
||||
|
||||
// SEL_RES (SAK) bits, as reported by InListPassiveTarget for ISO/IEC 14443 type A targets (NXP AN10833)
|
||||
static constexpr uint8_t SEL_RES_MIFARE_CLASSIC = 0x08;
|
||||
static constexpr uint8_t SEL_RES_ISO_DEP = 0x20;
|
||||
static constexpr uint8_t SEL_RES_TNP3XXX = 0x01; // MIFARE Classic 1K compatible
|
||||
|
||||
/// Tag type (nfc::TAG_TYPE_*) from a type A target's SEL_RES byte
|
||||
inline uint8_t tag_type_from_sel_res(uint8_t sel_res) {
|
||||
if ((sel_res & SEL_RES_MIFARE_CLASSIC) || sel_res == SEL_RES_TNP3XXX)
|
||||
return nfc::TAG_TYPE_MIFARE_CLASSIC;
|
||||
if (sel_res & SEL_RES_ISO_DEP)
|
||||
return nfc::TAG_TYPE_4;
|
||||
if (sel_res == 0x00)
|
||||
return nfc::TAG_TYPE_2;
|
||||
return nfc::TAG_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
class PN532BinarySensor;
|
||||
|
||||
class PN532 : public PollingComponent {
|
||||
@@ -67,11 +83,14 @@ class PN532 : public PollingComponent {
|
||||
virtual bool read_data(std::vector<uint8_t> &data, uint8_t len) = 0;
|
||||
virtual bool read_response(uint8_t command, std::vector<uint8_t> &data) = 0;
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(nfc::NfcTagUid &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(nfc::NfcTagUid &uid, uint8_t tag_type);
|
||||
|
||||
bool format_tag_(nfc::NfcTagUid &uid);
|
||||
bool clean_tag_(nfc::NfcTagUid &uid);
|
||||
bool write_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
bool format_tag_(nfc::NfcTagUid &uid, uint8_t tag_type);
|
||||
bool clean_tag_(nfc::NfcTagUid &uid, uint8_t tag_type);
|
||||
bool write_tag_(nfc::NfcTagUid &uid, uint8_t tag_type, nfc::NdefMessage *message);
|
||||
/// Sends an InDataExchange command and reads the response; returns false unless the status byte reports success.
|
||||
/// On success, `response` holds the data returned by the target, without the status byte.
|
||||
bool in_data_exchange_(const std::vector<uint8_t> &command, std::vector<uint8_t> &response);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(nfc::NfcTagUid &uid);
|
||||
bool read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
@@ -91,27 +110,32 @@ class PN532 : public PollingComponent {
|
||||
bool write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
bool clean_mifare_ultralight_();
|
||||
|
||||
bool updates_enabled_{true};
|
||||
bool requested_read_{false};
|
||||
std::vector<PN532BinarySensor *> binary_sensors_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
nfc::NfcTagUid current_uid_;
|
||||
nfc::NdefMessage *next_task_message_to_write_;
|
||||
optional<uint32_t> rd_start_time_{};
|
||||
enum PN532ReadReady rd_ready_ { WOULDBLOCK };
|
||||
enum NfcTask {
|
||||
enum NfcTask : uint8_t {
|
||||
READ = 0,
|
||||
CLEAN,
|
||||
FORMAT,
|
||||
WRITE,
|
||||
} next_task_{READ};
|
||||
enum PN532Error {
|
||||
};
|
||||
enum PN532Error : uint8_t {
|
||||
NONE = 0,
|
||||
WAKEUP_FAILED,
|
||||
SAM_COMMAND_FAILED,
|
||||
} error_code_{NONE};
|
||||
};
|
||||
|
||||
// members are ordered by alignment, widest first, to minimize padding
|
||||
CallbackManager<void()> on_finished_write_callback_;
|
||||
std::vector<PN532BinarySensor *> binary_sensors_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
std::unique_ptr<nfc::NdefMessage> next_task_message_to_write_;
|
||||
nfc::NfcTagUid current_uid_;
|
||||
uint32_t rd_start_time_{0}; // valid only while rd_started_ is set
|
||||
PN532ReadReady rd_ready_{WOULDBLOCK};
|
||||
NfcTask next_task_{READ};
|
||||
PN532Error error_code_{NONE};
|
||||
bool rd_started_{false};
|
||||
bool updates_enabled_{true};
|
||||
bool requested_read_{false};
|
||||
};
|
||||
|
||||
class PN532BinarySensor final : public binary_sensor::BinarySensor {
|
||||
|
||||
@@ -36,14 +36,15 @@ std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(nfc::NfcTagUid &uid
|
||||
if (nfc::mifare_classic_is_first_block(current_block)) {
|
||||
if (!this->auth_mifare_classic_block_(uid, current_block, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY)) {
|
||||
ESP_LOGE(TAG, "Error, Block authentication failed for %d", current_block);
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
|
||||
}
|
||||
}
|
||||
std::vector<uint8_t> block_data;
|
||||
if (this->read_mifare_classic_block_(current_block, block_data)) {
|
||||
buffer.insert(buffer.end(), block_data.begin(), block_data.end());
|
||||
} else {
|
||||
if (!this->read_mifare_classic_block_(current_block, block_data)) {
|
||||
ESP_LOGE(TAG, "Error reading block %d", current_block);
|
||||
return make_unique<nfc::NfcTag>(uid, nfc::MIFARE_CLASSIC);
|
||||
}
|
||||
buffer.insert(buffer.end(), block_data.begin(), block_data.end());
|
||||
|
||||
index += nfc::MIFARE_CLASSIC_BLOCK_SIZE;
|
||||
current_block++;
|
||||
@@ -63,20 +64,18 @@ std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(nfc::NfcTagUid &uid
|
||||
}
|
||||
|
||||
bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data) {
|
||||
if (!this->write_command_({
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
nfc::MIFARE_CMD_READ,
|
||||
block_num,
|
||||
})) {
|
||||
if (!this->in_data_exchange_(
|
||||
{
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
nfc::MIFARE_CMD_READ,
|
||||
block_num,
|
||||
},
|
||||
data) ||
|
||||
data.size() != nfc::MIFARE_CLASSIC_BLOCK_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, data) || data[0] != 0x00) {
|
||||
return false;
|
||||
}
|
||||
data.erase(data.begin());
|
||||
|
||||
char data_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
ESP_LOGVV(TAG, " Block %d: %s", block_num, nfc::format_bytes_to(data_buf, data));
|
||||
return true;
|
||||
@@ -90,14 +89,14 @@ bool PN532::auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, u
|
||||
block_num, // Block number
|
||||
});
|
||||
data.insert(data.end(), key, key + 6);
|
||||
data.insert(data.end(), uid.begin(), uid.end());
|
||||
if (!this->write_command_(data)) {
|
||||
ESP_LOGE(TAG, "Authentication failed - Block %d", block_num);
|
||||
// the command takes exactly 4 UID bytes (UM0701-02, 7.3.8); for 7-byte UIDs these are the last 4, as in libnfc
|
||||
if (uid.size() < 4) {
|
||||
return false;
|
||||
}
|
||||
data.insert(data.end(), uid.end() - 4, uid.end());
|
||||
|
||||
std::vector<uint8_t> response;
|
||||
if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) {
|
||||
if (!this->in_data_exchange_(data, response)) {
|
||||
ESP_LOGE(TAG, "Authentication failed - Block 0x%02x", block_num);
|
||||
return false;
|
||||
}
|
||||
@@ -167,6 +166,8 @@ bool PN532::format_mifare_classic_ndef_(nfc::NfcTagUid &uid) {
|
||||
|
||||
ESP_LOGD(TAG, "Sector 0 formatted to NDEF");
|
||||
|
||||
bool error = false;
|
||||
|
||||
for (int block = 4; block < 64; block += 4) {
|
||||
if (!this->auth_mifare_classic_block_(uid, block + 3, nfc::MIFARE_CMD_AUTH_B, nfc::DEFAULT_KEY)) {
|
||||
return false;
|
||||
@@ -174,23 +175,28 @@ bool PN532::format_mifare_classic_ndef_(nfc::NfcTagUid &uid) {
|
||||
if (block == 4) {
|
||||
if (!this->write_mifare_classic_block_(block, EMPTY_NDEF_MESSAGE.data(), EMPTY_NDEF_MESSAGE.size())) {
|
||||
ESP_LOGE(TAG, "Unable to write block %d", block);
|
||||
error = true;
|
||||
}
|
||||
} else {
|
||||
if (!this->write_mifare_classic_block_(block, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
|
||||
ESP_LOGE(TAG, "Unable to write block %d", block);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
if (!this->write_mifare_classic_block_(block + 1, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
|
||||
ESP_LOGE(TAG, "Unable to write block %d", block + 1);
|
||||
error = true;
|
||||
}
|
||||
if (!this->write_mifare_classic_block_(block + 2, BLANK_BLOCK.data(), BLANK_BLOCK.size())) {
|
||||
ESP_LOGE(TAG, "Unable to write block %d", block + 2);
|
||||
error = true;
|
||||
}
|
||||
if (!this->write_mifare_classic_block_(block + 3, NDEF_TRAILER.data(), NDEF_TRAILER.size())) {
|
||||
ESP_LOGE(TAG, "Unable to write trailer block %d", block + 3);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool PN532::write_mifare_classic_block_(uint8_t block_num, const uint8_t *data, size_t len) {
|
||||
@@ -201,13 +207,9 @@ bool PN532::write_mifare_classic_block_(uint8_t block_num, const uint8_t *data,
|
||||
block_num,
|
||||
});
|
||||
cmd.insert(cmd.end(), data, data + len);
|
||||
if (!this->write_command_(cmd)) {
|
||||
ESP_LOGE(TAG, "Error writing block %d", block_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> response;
|
||||
if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) {
|
||||
if (!this->in_data_exchange_(cmd, response)) {
|
||||
ESP_LOGE(TAG, "Error writing block %d", block_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
@@ -51,24 +52,20 @@ bool PN532::read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes
|
||||
std::vector<uint8_t> response;
|
||||
|
||||
for (uint8_t i = 0; i * read_increment < num_bytes; i++) {
|
||||
if (!this->write_command_({
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
nfc::MIFARE_CMD_READ,
|
||||
uint8_t(i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page),
|
||||
})) {
|
||||
// a READ returns 4 pages (16 bytes)
|
||||
if (!this->in_data_exchange_(
|
||||
{
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
nfc::MIFARE_CMD_READ,
|
||||
uint8_t(i * nfc::MIFARE_ULTRALIGHT_READ_SIZE + start_page),
|
||||
},
|
||||
response) ||
|
||||
response.size() != read_increment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response) || response[0] != 0x00) {
|
||||
return false;
|
||||
}
|
||||
uint16_t bytes_offset = (i + 1) * read_increment;
|
||||
auto pages_in_end_itr = bytes_offset <= num_bytes ? response.end() : response.end() - (bytes_offset - num_bytes);
|
||||
|
||||
if ((pages_in_end_itr > response.begin()) && (pages_in_end_itr <= response.end())) {
|
||||
data.insert(data.end(), response.begin() + 1, pages_in_end_itr);
|
||||
}
|
||||
const uint16_t remaining = num_bytes - i * read_increment;
|
||||
data.insert(data.end(), response.begin(), response.begin() + std::min<uint16_t>(read_increment, remaining));
|
||||
}
|
||||
|
||||
char data_buf[nfc::FORMAT_BYTES_BUFFER_SIZE];
|
||||
@@ -87,7 +84,7 @@ bool PN532::is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_t
|
||||
|
||||
uint16_t PN532::read_mifare_ultralight_capacity_() {
|
||||
std::vector<uint8_t> data;
|
||||
if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE, data)) {
|
||||
if (this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE, data) && data.size() > 2) {
|
||||
ESP_LOGV(TAG, "Tag capacity is %u bytes", data[2] * 8U);
|
||||
return data[2] * 8U;
|
||||
}
|
||||
@@ -174,13 +171,9 @@ bool PN532::write_mifare_ultralight_page_(uint8_t page_num, const uint8_t *write
|
||||
page_num,
|
||||
});
|
||||
cmd.insert(cmd.end(), write_data, write_data + len);
|
||||
if (!this->write_command_(cmd)) {
|
||||
ESP_LOGE(TAG, "Error writing page %u", page_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> response;
|
||||
if (!this->read_response(PN532_COMMAND_INDATAEXCHANGE, response)) {
|
||||
if (!this->in_data_exchange_(cmd, response)) {
|
||||
ESP_LOGE(TAG, "Error writing page %u", page_num);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -12,11 +12,12 @@ namespace esphome::pn532_i2c {
|
||||
static const char *const TAG = "pn532_i2c";
|
||||
|
||||
bool PN532I2C::is_read_ready() {
|
||||
uint8_t ready;
|
||||
if (!this->read_bytes_raw(&ready, 1)) {
|
||||
uint8_t status;
|
||||
if (!this->read_bytes_raw(&status, 1)) {
|
||||
return false;
|
||||
}
|
||||
return ready == 0x01;
|
||||
// only bit 0 (RDY) of the status byte is defined (UM0701-02, 6.2.4)
|
||||
return status & 0x01;
|
||||
}
|
||||
|
||||
bool PN532I2C::write_data(const std::vector<uint8_t> &data) {
|
||||
@@ -30,9 +31,9 @@ bool PN532I2C::read_data(std::vector<uint8_t> &data, uint8_t len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the PN532 prefixes every frame with a status byte
|
||||
data.resize(len + 1);
|
||||
this->read_bytes_raw(data.data(), len + 1);
|
||||
return true;
|
||||
return this->read_bytes_raw(data.data(), len + 1);
|
||||
}
|
||||
|
||||
bool PN532I2C::read_response(uint8_t command, std::vector<uint8_t> &data) {
|
||||
@@ -73,7 +74,7 @@ bool PN532I2C::read_response(uint8_t command, std::vector<uint8_t> &data) {
|
||||
checksum = ~checksum + 1;
|
||||
|
||||
if (data[len + 1] != checksum) {
|
||||
ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len], checksum);
|
||||
ESP_LOGV(TAG, "read data invalid checksum! %02X != %02X", data[len + 1], checksum);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ void PN532Spi::setup() {
|
||||
bool PN532Spi::is_read_ready() {
|
||||
this->enable();
|
||||
this->write_byte(0x02);
|
||||
bool ready = this->read_byte() == 0x01;
|
||||
// only bit 0 (RDY) of the status byte is defined (UM0701-02, 6.2.5)
|
||||
const bool ready = this->read_byte() & 0x01;
|
||||
this->disable();
|
||||
return ready;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "esphome/components/pn532/pn532.h"
|
||||
|
||||
namespace esphome::pn532 {
|
||||
|
||||
namespace {
|
||||
|
||||
// Stands in for the bus: acknowledges every command and answers with queued response payloads.
|
||||
class FakePN532 : public PN532 {
|
||||
public:
|
||||
using PN532::auth_mifare_classic_block_;
|
||||
using PN532::read_mifare_ultralight_bytes_;
|
||||
using PN532::read_mifare_classic_block_;
|
||||
using PN532::write_mifare_classic_block_;
|
||||
using PN532::write_mifare_ultralight_page_;
|
||||
|
||||
std::deque<std::vector<uint8_t>> responses;
|
||||
std::vector<std::vector<uint8_t>> written;
|
||||
|
||||
protected:
|
||||
bool is_read_ready() override { return true; }
|
||||
bool write_data(const std::vector<uint8_t> &data) override {
|
||||
this->written.push_back(data);
|
||||
return true;
|
||||
}
|
||||
// only used for ACK frames; index 0 is the I2C status byte
|
||||
bool read_data(std::vector<uint8_t> &data, uint8_t len) override {
|
||||
data = {0x01, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};
|
||||
return true;
|
||||
}
|
||||
bool read_response(uint8_t command, std::vector<uint8_t> &data) override {
|
||||
if (this->responses.empty())
|
||||
return false;
|
||||
data = this->responses.front();
|
||||
this->responses.pop_front();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Extracts the command bytes (after TFI) from a normal information frame
|
||||
std::vector<uint8_t> frame_data(const std::vector<uint8_t> &frame) {
|
||||
// preamble, start code (2), LEN, LCS, TFI, data..., DCS, postamble
|
||||
return std::vector<uint8_t>(frame.begin() + 6, frame.end() - 2);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(PN532TagType, FromSelRes) {
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x08), nfc::TAG_TYPE_MIFARE_CLASSIC); // Classic 1K
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x18), nfc::TAG_TYPE_MIFARE_CLASSIC); // Classic 4K
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x09), nfc::TAG_TYPE_MIFARE_CLASSIC); // Mini
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x01), nfc::TAG_TYPE_MIFARE_CLASSIC); // TNP3xxx
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x00), nfc::TAG_TYPE_2); // Ultralight / NTAG
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x20), nfc::TAG_TYPE_4); // ISO-DEP (phones, DESFire)
|
||||
EXPECT_EQ(tag_type_from_sel_res(0x40), nfc::TAG_TYPE_UNKNOWN);
|
||||
}
|
||||
|
||||
// A failed write (status byte other than 0x00) must be reported as a failure.
|
||||
TEST(PN532Mifare, ClassicWriteChecksStatus) {
|
||||
FakePN532 pn532;
|
||||
const uint8_t block[16] = {};
|
||||
pn532.responses.push_back({0x14}); // authentication error
|
||||
EXPECT_FALSE(pn532.write_mifare_classic_block_(4, block, sizeof(block)));
|
||||
pn532.responses.push_back({0x00});
|
||||
EXPECT_TRUE(pn532.write_mifare_classic_block_(4, block, sizeof(block)));
|
||||
}
|
||||
|
||||
TEST(PN532Mifare, UltralightWriteChecksStatus) {
|
||||
FakePN532 pn532;
|
||||
const uint8_t page[4] = {};
|
||||
pn532.responses.push_back({0x01}); // timeout
|
||||
EXPECT_FALSE(pn532.write_mifare_ultralight_page_(4, page, sizeof(page)));
|
||||
pn532.responses.push_back({0x00});
|
||||
EXPECT_TRUE(pn532.write_mifare_ultralight_page_(4, page, sizeof(page)));
|
||||
}
|
||||
|
||||
TEST(PN532Mifare, ClassicReadRejectsBadResponses) {
|
||||
FakePN532 pn532;
|
||||
std::vector<uint8_t> data;
|
||||
pn532.responses.emplace_back(); // empty response
|
||||
EXPECT_FALSE(pn532.read_mifare_classic_block_(4, data));
|
||||
data.clear();
|
||||
pn532.responses.push_back({0x00, 0x01, 0x02}); // short block
|
||||
EXPECT_FALSE(pn532.read_mifare_classic_block_(4, data));
|
||||
|
||||
std::vector<uint8_t> good(17, 0xAB);
|
||||
good[0] = 0x00;
|
||||
pn532.responses.push_back(good);
|
||||
data.clear();
|
||||
EXPECT_TRUE(pn532.read_mifare_classic_block_(4, data));
|
||||
EXPECT_EQ(data, std::vector<uint8_t>(16, 0xAB));
|
||||
}
|
||||
|
||||
// Authentication carries exactly 4 UID bytes: the last 4 of a 7-byte UID.
|
||||
TEST(PN532Mifare, AuthSendsFourUidBytes) {
|
||||
FakePN532 pn532;
|
||||
nfc::NfcTagUid uid = {0x04, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
|
||||
pn532.responses.push_back({0x00});
|
||||
EXPECT_TRUE(pn532.auth_mifare_classic_block_(uid, 4, nfc::MIFARE_CMD_AUTH_A, nfc::NDEF_KEY));
|
||||
ASSERT_EQ(pn532.written.size(), 1u);
|
||||
const auto cmd = frame_data(pn532.written[0]);
|
||||
// InDataExchange, Tg, Cmd, Addr, key (6), UID (4)
|
||||
ASSERT_EQ(cmd.size(), 14u);
|
||||
EXPECT_EQ(std::vector<uint8_t>(cmd.end() - 4, cmd.end()), (std::vector<uint8_t>{0x33, 0x44, 0x55, 0x66}));
|
||||
}
|
||||
|
||||
// Reads in 16-byte chunks, keeps only the bytes asked for, and advances 4 pages per READ.
|
||||
TEST(PN532Mifare, UltralightReadTrimsLastChunk) {
|
||||
FakePN532 pn532;
|
||||
std::vector<uint8_t> first(17), second(17);
|
||||
first[0] = second[0] = 0x00; // status
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
first[i + 1] = i;
|
||||
second[i + 1] = 0x10 + i;
|
||||
}
|
||||
pn532.responses.push_back(first);
|
||||
pn532.responses.push_back(second);
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
ASSERT_TRUE(pn532.read_mifare_ultralight_bytes_(4, 20, data));
|
||||
ASSERT_EQ(data.size(), 20u);
|
||||
EXPECT_EQ(data[15], 15);
|
||||
EXPECT_EQ(data[16], 0x10);
|
||||
EXPECT_EQ(data[19], 0x13);
|
||||
|
||||
ASSERT_EQ(pn532.written.size(), 2u);
|
||||
EXPECT_EQ(frame_data(pn532.written[0]).back(), 4); // READ page 4
|
||||
EXPECT_EQ(frame_data(pn532.written[1]).back(), 8); // then page 8
|
||||
}
|
||||
|
||||
TEST(PN532Mifare, UltralightReadRejectsBadResponses) {
|
||||
FakePN532 pn532;
|
||||
std::vector<uint8_t> data;
|
||||
pn532.responses.push_back({0x00, 0x01, 0x02}); // short response
|
||||
EXPECT_FALSE(pn532.read_mifare_ultralight_bytes_(4, 16, data));
|
||||
|
||||
std::vector<uint8_t> failed(17, 0x00);
|
||||
failed[0] = 0x01; // timeout status
|
||||
pn532.responses.push_back(failed);
|
||||
data.clear();
|
||||
EXPECT_FALSE(pn532.read_mifare_ultralight_bytes_(4, 16, data));
|
||||
}
|
||||
|
||||
} // namespace esphome::pn532
|
||||
Reference in New Issue
Block a user