Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,33 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
add_subdirectory(pn532_serial)
target_include_directories(${MODULE_NAME}
PRIVATE
"common"
"pn532_serial"
)
target_link_libraries(${MODULE_NAME}
PRIVATE
everest::helpers
pn532_serial
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"main/auth_token_providerImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 - 2022 Pionix GmbH and Contributors to EVerest
#include "PN532TokenProvider.hpp"
namespace module {
void PN532TokenProvider::init() {
invoke_init(*p_main);
}
void PN532TokenProvider::ready() {
invoke_ready(*p_main);
}
} // namespace module

View File

@@ -0,0 +1,58 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef PN532TOKEN_PROVIDER_HPP
#define PN532TOKEN_PROVIDER_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {};
class PN532TokenProvider : public Everest::ModuleBase {
public:
PN532TokenProvider() = delete;
PN532TokenProvider(const ModuleInfo& info, std::unique_ptr<auth_token_providerImplBase> p_main, Conf& config) :
ModuleBase(info), p_main(std::move(p_main)), config(config){};
const std::unique_ptr<auth_token_providerImplBase> p_main;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
// insert other definitions here
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // PN532TOKEN_PROVIDER_HPP

View File

@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "auth_token_providerImpl.hpp"
#include <everest/helpers/helpers.hpp>
#include <fmt/core.h>
namespace module {
namespace main {
void auth_token_providerImpl::init() {
// initialize serial driver
if (config.debug) {
serial.enableDebug();
EVLOG_info << "Serial port: " << config.serial_port << " baud rate: " << config.baud_rate;
}
if (!serial.openDevice(config.serial_port.c_str(), config.baud_rate)) {
if (!this->error_state_monitor->is_error_active("generic/CommunicationFault", "Communication timed out")) {
auto error_message =
fmt::format("Could not open serial port {} with baud rate {}", config.serial_port, config.baud_rate);
auto error = this->error_factory->create_error("generic/CommunicationFault", "Communication timed out",
error_message);
raise_error(error);
}
return;
}
}
void auth_token_providerImpl::ready() {
serial.run();
serial.reset();
// configure Secure Access Module (SAM)
auto configure_sam_future = serial.configureSAM();
while (configure_sam_future.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready) {
EVLOG_verbose << "Retrying to configure SAM";
configure_sam_future = serial.configureSAM();
}
auto configure_sam = configure_sam_future.get();
if (configure_sam) {
EVLOG_debug << "Configured SAM";
}
auto firmware_version_future = serial.getFirmwareVersion();
while (firmware_version_future.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready) {
EVLOG_verbose << "Retrying to get firmware version";
firmware_version_future = serial.getFirmwareVersion();
}
auto firmware_version = firmware_version_future.get();
if (firmware_version.valid) {
std::shared_ptr<FirmwareVersion> fv = std::dynamic_pointer_cast<FirmwareVersion>(firmware_version.message);
EVLOG_info << "PN532 firmware version: " << std::to_string(fv->ver) << "." << std::to_string(fv->rev);
}
while (true) {
auto in_list_passive_target_response_future = serial.inListPassiveTarget();
while (in_list_passive_target_response_future.wait_for(std::chrono::seconds(5)) != std::future_status::ready) {
EVLOG_verbose << "Retrying to get target";
in_list_passive_target_response_future = serial.inListPassiveTarget();
}
auto in_list_passive_target_response = in_list_passive_target_response_future.get();
if (in_list_passive_target_response.valid) {
std::shared_ptr<InListPassiveTargetResponse> in_list_passive_target_response_message =
std::dynamic_pointer_cast<InListPassiveTargetResponse>(in_list_passive_target_response.message);
auto target_data = in_list_passive_target_response_message->target_data;
for (auto entry : target_data) {
types::authorization::ProvidedIdToken provided_token;
provided_token.id_token = {entry.getNFCID(), types::authorization::IdTokenType::ISO14443};
provided_token.authorization_type = types::authorization::AuthorizationType::RFID;
if (config.debug) {
EVLOG_info << "Publishing new rfid/nfc token: " << everest::helpers::redact(provided_token);
}
this->publish_provided_token(provided_token);
}
}
std::this_thread::sleep_for(std::chrono::seconds(config.read_timeout));
}
}
} // namespace main
} // namespace module

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP
#define MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/auth_token_provider/Implementation.hpp>
#include "../PN532TokenProvider.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
#include "pn532_serial/PN532Serial.h"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace main {
struct Conf {
std::string serial_port;
int baud_rate;
int read_timeout;
bool debug;
};
class auth_token_providerImpl : public auth_token_providerImplBase {
public:
auth_token_providerImpl() = delete;
auth_token_providerImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<PN532TokenProvider>& mod,
Conf& config) :
auth_token_providerImplBase(ev, "main"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// no commands defined for this interface
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<PN532TokenProvider>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
PN532Serial serial;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace main
} // namespace module
#endif // MAIN_AUTH_TOKEN_PROVIDER_IMPL_HPP

View File

@@ -0,0 +1,33 @@
description: PN532 RFID/NFC token provider returning the token as soon as the tag can be read by the reader
provides:
main:
description: Implementation of PN532 RFID/NFC token provider
interface: auth_token_provider
config:
serial_port:
description: Serial port the PN532 hardware is connected to
type: string
default: /dev/ttyS0
baud_rate:
description: Serial baud rate to use when communicating with PN532 hardware
type: integer
minimum: 9600
maximum: 230400
default: 115200
read_timeout:
description: Time between subsequent card reads (in s)
type: integer
minimum: 0
maximum: 120
default: 5
debug:
description: Show debug output on command line.
type: boolean
default: false
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen
- Kai-Uwe Hermann
- Thilo Molitor
- Anton Wöllert

View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.10)
# set the project name
project(pn532_serial VERSION 0.1)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# add the executable
add_library(pn532_serial STATIC PN532Serial.cpp)
ev_register_library_target(pn532_serial)
target_include_directories(pn532_serial PUBLIC "${PROJECT_BINARY_DIR}")
target_link_libraries(pn532_serial PRIVATE Threads::Threads everest::framework)

View File

@@ -0,0 +1,331 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#include "PN532Serial.h"
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <string.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <everest/logging.hpp>
constexpr size_t FIRMWARE_VERSION_IC_POS = 0;
constexpr size_t FIRMWARE_VERSION_VER_POS = 1;
constexpr size_t FIRMWARE_VERSION_REV_POS = 2;
constexpr size_t FIRMWARE_VERSION_SUPPORT_POS = 3;
constexpr size_t IN_LIST_PASSIVE_TARGET_NBTG = 0;
constexpr size_t IN_LIST_PASSIVE_TARGET_TG = 1;
constexpr size_t IN_LIST_PASSIVE_TARGET_SENS_RES_MSB = 2;
constexpr size_t IN_LIST_PASSIVE_TARGET_SENS_RES_LSB = 3;
constexpr size_t IN_LIST_PASSIVE_TARGET_SEL_RES = 4;
constexpr size_t IN_LIST_PASSIVE_TARGET_NFCID_LENGTH = 5;
// command codes
constexpr uint8_t COMMAND_GET_FIRMWARE_VERSION = 0x02;
constexpr uint8_t COMMAND_GET_FIRMWARE_VERSION_RESPONSE = 0x03;
constexpr uint8_t COMMAND_SAM_CONFIGURATION = 0x14;
constexpr uint8_t COMMAND_SAM_CONFIGURATION_RESPONSE = 0x15;
constexpr uint8_t COMMAND_IN_LIST_PASSIVE_TARGET = 0x4a;
constexpr uint8_t COMMAND_IN_LIST_PASSIVE_TARGET_RESPONSE = 0x4b;
void PN532Serial::run() {
readThreadHandle = std::thread(&PN532Serial::readThread, this);
}
void PN532Serial::readThread() {
uint8_t buf[2048];
size_t n = 0;
ssize_t first_start_code_offset;
while (true) {
if (readThreadHandle.shouldExit()) {
break;
}
auto c = read(fd, &buf[n], sizeof(buf) - n);
if (c == -1) {
EVLOG_error << "Read failed: " << strerror(errno) << " (" << errno << ")";
break;
}
if (c == 0) {
continue;
}
if (this->debug) {
EVLOG_info << "Received: " << hexdump(buf + n, c);
}
n += c;
restart_buffer_analysis:
EVLOG_verbose << "buffer content (size: " << n << "): " << hexdump(buf, n);
// packet minimum size is 6 byte for ACK or NACK, other frames are larger
if (n < 6) {
continue;
}
first_start_code_offset = -1;
// we cannot assume that we receive the whole response in one read call
// because of different driver/interfaces, so we have to scan for well-known
// patterns or check whether it looks like a valid frame after a start code
// pattern; if we find one, we can delete it from the buffer and move the
// left-over buffer content to the front, then we must restart analyzing
// to be sure to handle any following frame immediately and don't wait
// until the next read call runs/returns
for (ssize_t i = 0; i <= n - 6; ++i) {
// check for ACK or NACKs
if (memcmp(&buf[i], ACK_FRAME.data(), ACK_FRAME.size()) == 0) {
EVLOG_debug << "Received ACK";
n = n - i - ACK_FRAME.size();
memmove(buf, buf + i + ACK_FRAME.size(), n);
goto restart_buffer_analysis;
}
if (memcmp(&buf[i], NACK_FRAME.data(), NACK_FRAME.size()) == 0) {
EVLOG_debug << "Received NACK";
n = n - i - NACK_FRAME.size();
memmove(buf, buf + i + NACK_FRAME.size(), n);
goto restart_buffer_analysis;
}
// check for start_code
if (memcmp(&buf[i], START_CODE.data(), START_CODE.size()) == 0) {
if (first_start_code_offset == -1) {
first_start_code_offset = i;
}
auto len = buf[i + START_CODE.size()];
auto lcs = buf[i + START_CODE.size() + 1];
if (((len + lcs) & 0xff) != 0) {
EVLOG_verbose << "package length checksum mismatch";
continue;
}
// offset + start code size + len and lcs byte + data length + dcs and postamble
if (i + START_CODE.size() + 2 + len + 2 > n) {
EVLOG_verbose << "frame still too short or length bogus";
continue;
}
// accesses beyond i are safe below since we ensure that above
auto tfi = buf[i + START_CODE.size() + 2];
if (tfi != PN532_TO_HOST) {
EVLOG_verbose << "frame is not a response";
continue;
}
if (buf[i + START_CODE.size() + 2 + len + 1] != 0x00) {
EVLOG_verbose << "postamble check failed";
continue;
}
auto dcs_compl = 0;
for (size_t j = 0; j < len; ++j) {
dcs_compl += buf[i + START_CODE.size() + 2 + j];
}
auto dcs = buf[i + START_CODE.size() + 2 + len];
if (((dcs_compl + dcs) & 0xff) != 0) {
EVLOG_verbose << "package data checksum does not match";
continue;
}
// all checks so far indicate that frame/packet looks good
command_code = buf[i + START_CODE.size() + 2 + 1];
data = std::vector<uint8_t>(&buf[i + START_CODE.size() + 2 + 1 + 1],
&buf[i + START_CODE.size() + 2 + len]);
parseData();
// discard processed data, the processed size is:
// offset (incl. preamble) + start code size + len and lcs byte + data length + dcs and postamble
auto processed_size = i + START_CODE.size() + 1 + 1 + data.size() + 1 + 1;
n = n - processed_size;
memmove(buf, buf + processed_size, n);
goto restart_buffer_analysis;
}
}
if (first_start_code_offset == -1) {
// no start code found so far: continue with outer loop and receive more data
} else {
// use first start code offset found and assume garbage before -> discard this
n -= first_start_code_offset;
memmove(buf, buf + first_start_code_offset, n);
}
}
}
void PN532Serial::parseData() {
if (command_code == COMMAND_GET_FIRMWARE_VERSION_RESPONSE && this->get_firmware_version_promise) {
PN532Response response;
if (data.size() == 4) {
EVLOG_debug << "Sending ACK";
serialWrite(ACK_FRAME);
response.valid = true;
response.message = std::make_shared<FirmwareVersion>(
data.at(FIRMWARE_VERSION_IC_POS), data.at(FIRMWARE_VERSION_VER_POS), data.at(FIRMWARE_VERSION_REV_POS),
data.at(FIRMWARE_VERSION_SUPPORT_POS));
this->get_firmware_version_promise->set_value(response);
} else {
EVLOG_debug << "Sending NACK";
serialWrite(NACK_FRAME);
}
} else if (command_code == COMMAND_SAM_CONFIGURATION_RESPONSE && this->configure_sam_promise) {
EVLOG_debug << "Sending ACK";
serialWrite(ACK_FRAME);
this->configure_sam_promise->set_value(true);
} else if (command_code == COMMAND_IN_LIST_PASSIVE_TARGET_RESPONSE && this->in_list_passive_target_promise) {
// always send ACK for now
EVLOG_debug << "Sending ACK";
serialWrite(ACK_FRAME);
parseInListPassiveTargetResponse();
}
}
void PN532Serial::parseInListPassiveTargetResponse() {
PN532Response response;
auto in_list_passive_target_response = std::make_shared<InListPassiveTargetResponse>();
TargetData target_data;
if (data.size() >= 6) {
in_list_passive_target_response->nbtg = data.at(IN_LIST_PASSIVE_TARGET_NBTG);
if (in_list_passive_target_response->nbtg == 1) {
target_data.tg = data.at(IN_LIST_PASSIVE_TARGET_TG);
target_data.sens_res_msb = data.at(IN_LIST_PASSIVE_TARGET_SENS_RES_MSB);
target_data.sens_res_lsb = data.at(IN_LIST_PASSIVE_TARGET_SENS_RES_LSB);
target_data.sens_res = (target_data.sens_res_msb << 8) + target_data.sens_res_lsb;
target_data.sel_res = data.at(IN_LIST_PASSIVE_TARGET_SEL_RES);
target_data.nfcid_length = data.at(IN_LIST_PASSIVE_TARGET_NFCID_LENGTH);
if (this->debug) {
EVLOG_info << "Target data: " << std::right << std::hex << "\ntg: 0x" << std::setfill('0')
<< std::setw(2) << (int)target_data.tg << "\nsens_res_msb: 0x" << std::setfill('0')
<< std::setw(2) << (int)target_data.sens_res_msb << "\nsens_res_lsb: 0x" << std::setfill('0')
<< std::setw(2) << (int)target_data.sens_res_lsb << "\nsens_res: 0x" << std::setfill('0')
<< std::setw(4) << (int)target_data.sens_res << "\nsel_res: 0x" << std::setfill('0')
<< std::setw(2) << (int)target_data.sel_res << "\nnfcid_length: 0x" << std::setfill('0')
<< std::setw(2) << (int)target_data.nfcid_length;
}
if (data.size() >= 6 + target_data.nfcid_length) {
response.valid = true;
for (ssize_t i = 6; i < 6 + target_data.nfcid_length; i++) {
target_data.nfcid.push_back(data.at(i));
}
if (this->debug) {
std::stringstream nfcid_stream;
nfcid_stream << hexdump(target_data.nfcid);
nfcid_stream << " (length: " << std::dec << target_data.nfcid.size() << ")";
EVLOG_info << "NFCID: " << nfcid_stream.str();
}
if (data.size() >= 6 + target_data.nfcid_length + 1) {
auto ats_length = data.at(6 + target_data.nfcid_length);
if (data.size() >= 6 + target_data.nfcid_length + ats_length) {
for (size_t i = 6 + target_data.nfcid_length; i < 6 + target_data.nfcid_length + ats_length;
i++) {
target_data.ats.push_back(data.at(i));
}
}
if (this->debug) {
std::stringstream ats_stream;
ats_stream << hexdump(target_data.ats);
ats_stream << " (length: " << std::dec << target_data.ats.size() << ")";
EVLOG_info << "ATS: " << ats_stream.str();
}
}
}
in_list_passive_target_response->target_data.push_back(target_data);
}
}
response.message = in_list_passive_target_response;
this->in_list_passive_target_promise->set_value(response);
}
std::future<bool> PN532Serial::configureSAM() {
this->configure_sam_promise = std::make_unique<std::promise<bool>>();
this->serialWriteCommand({
COMMAND_SAM_CONFIGURATION, // command code
0x01, // normal mode
0x00, // no timeout
0x01 // P70_IRQ pin driven by PN532
});
return this->configure_sam_promise->get_future();
}
std::future<PN532Response> PN532Serial::getFirmwareVersion() {
this->get_firmware_version_promise = std::make_unique<std::promise<PN532Response>>();
this->serialWriteCommand({COMMAND_GET_FIRMWARE_VERSION});
return this->get_firmware_version_promise->get_future();
}
std::future<PN532Response> PN532Serial::inListPassiveTarget() {
this->in_list_passive_target_promise = std::make_unique<std::promise<PN532Response>>();
this->serialWriteCommand({
COMMAND_IN_LIST_PASSIVE_TARGET, // command code
0x01, // one target
0x00 // 105 kbps type A (ISO/IEC1443 Type A)
});
return this->in_list_passive_target_promise->get_future();
}
bool PN532Serial::serialWrite(const std::vector<uint8_t>& data) {
auto rv = write(fd, data.data(), data.size());
if (rv != data.size()) {
EVLOG_error << "Write failed: " << strerror(errno) << " (" << errno << ")";
return false;
}
return true;
}
bool PN532Serial::serialWriteCommand(const std::vector<uint8_t>& data) {
// calculate packet length checksum
uint8_t lcs = data.size() + 1;
lcs = (uint8_t)-lcs;
// calculate data check sum
uint8_t dcs = HOST_TO_PN532;
for (auto element : data) {
dcs += element;
}
dcs = (uint8_t)-dcs;
std::vector<uint8_t> serial_data;
// size: preamble + start code + LEN + LCS + TFI + data + DCS + postamble
serial_data.reserve(1 + START_CODE.size() + 1 + 1 + 1 + data.size() + 1 + 1);
serial_data.push_back(PREAMBLE);
serial_data.insert(serial_data.end(), START_CODE.begin(), START_CODE.end());
serial_data.push_back(data.size() + 1);
serial_data.push_back(lcs);
serial_data.push_back(HOST_TO_PN532);
serial_data.insert(serial_data.end(), data.begin(), data.end());
serial_data.push_back(dcs);
serial_data.push_back(POSTAMBLE);
if (this->debug) {
EVLOG_info << "Sending bytes: " << hexdump(serial_data);
}
return this->serialWrite(serial_data);
}
bool PN532Serial::reset() {
return this->serialWrite(
{0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
}
void PN532Serial::enableDebug() {
this->debug = true;
}

View File

@@ -0,0 +1,103 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2022 Pionix GmbH and Contributors to EVerest
#ifndef PN532_SERIAL
#define PN532_SERIAL
#include <future>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdint.h>
#include <termios.h>
#include <utils/serial.hpp>
#include <utils/thread.hpp>
#include <vector>
enum class PN532Command {
SAMConfiguration,
GetFirmwareVersion
};
struct PN532Message {
virtual ~PN532Message() = default;
};
struct PN532Response {
bool valid = false;
std::shared_ptr<PN532Message> message;
};
struct FirmwareVersion : public PN532Message {
uint8_t ic;
uint8_t ver; // TODO: also pre-parsed version string of this
uint8_t rev;
uint8_t support; // TODO: bool flags
FirmwareVersion(uint8_t ic, uint8_t ver, uint8_t rev, uint8_t support) :
ic(ic), ver(ver), rev(rev), support(support) {
}
};
struct TargetData {
uint8_t tg;
uint8_t sens_res_msb;
uint8_t sens_res_lsb;
uint16_t sens_res;
uint8_t sel_res;
uint8_t nfcid_length;
std::vector<uint8_t> nfcid;
std::vector<uint8_t> ats;
std::string getNFCID() {
std::stringstream ss;
for (auto it = nfcid.begin(); it != nfcid.end(); it++) {
int id = *it;
ss << std::setfill('0') << std::setw(2) << std::hex << id;
}
return ss.str();
};
};
struct InListPassiveTargetResponse : public PN532Message {
uint8_t nbtg = 0;
std::vector<TargetData> target_data;
};
class PN532Serial : public Everest::Serial {
public:
PN532Serial() = default;
virtual ~PN532Serial() = default;
void readThread();
void run() override;
bool reset();
bool serialWrite(const std::vector<uint8_t>& data);
bool serialWriteCommand(const std::vector<uint8_t>& data);
std::future<bool> configureSAM();
std::future<PN532Response> getFirmwareVersion();
std::future<PN532Response> inListPassiveTarget();
void enableDebug();
private:
static constexpr uint8_t PREAMBLE = 0x00;
static constexpr uint8_t POSTAMBLE = 0x00;
static constexpr uint8_t HOST_TO_PN532 = 0xd4;
static constexpr uint8_t PN532_TO_HOST = 0xd5;
const std::vector<uint8_t> START_CODE{0x00, 0xff};
const std::vector<uint8_t> ACK_FRAME{0x00, 0x00, 0xff, 0x00, 0xff, 0x00};
const std::vector<uint8_t> NACK_FRAME{0x00, 0x00, 0xff, 0xff, 0x00, 0x00};
std::unique_ptr<std::promise<bool>> configure_sam_promise;
std::unique_ptr<std::promise<PN532Response>> get_firmware_version_promise;
std::unique_ptr<std::promise<PN532Response>> in_list_passive_target_promise;
uint8_t command_code = 0;
std::vector<uint8_t> data;
bool debug = false;
void parseData();
void parseInListPassiveTargetResponse();
// Read thread for serial port
Everest::Thread readThreadHandle;
};
#endif