Files
filamentguard_esp32/src/Pn5180Reader.cpp
Bjarne Pedersen d757e13ce6 Version 1 færdig
Læser og kalder API som det skal.
OTA virker
Opsætning af wifi via ESP32's eget hotspot virker

Mangler at få lavet skrivning, men det bliver v2.
2026-07-27 14:33:06 +02:00

995 lines
20 KiB
C++

#include "Pn5180Reader.h"
#include <cstring>
namespace
{
// Antal gange getInventory forsoeges pr. scan, foer vi giver op.
// ISO15693 er slot-baseret og et enkelt scan kan sagtens misse
// et svag-koblet tag i sit slot. Retries er standardpraksis.
constexpr uint8_t MaxInventoryAttempts = 3;
constexpr uint16_t InventoryRetryDelayMilliseconds = 10;
// Tid RF-feltet skal vaere aktivt foer inventory sendes, saa
// passive tags naar at faa opladet deres kondensator.
constexpr uint16_t RfSettleDelayMilliseconds = 20;
bool isValidIso15693Uid(
const uint8_t* uid)
{
if (uid == nullptr)
{
return false;
}
// ISO15693-UID'er leveres LSB foerst af PN5180. Den sidste byte
// skal derfor vaere ISO/IEC 15963-markoeren 0xE0. Kontrollen
// afviser blandt andet bibliotekets falske nul-UID.
return uid[7] == 0xE0;
}
}
Pn5180Reader::Pn5180Reader(
FilamentGuardLogger& newLogger,
const uint8_t nssPin,
const uint8_t busyPin,
const uint8_t resetPin)
: iso15693Reader(
nssPin,
busyPin,
resetPin),
logger(newLogger)
{
}
void Pn5180Reader::begin()
{
iso15693Reader.begin();
iso15693Reader.reset();
uint8_t dieIdentifier[16] = {};
if (!iso15693Reader.readEEprom(
DIE_IDENTIFIER,
dieIdentifier,
sizeof(dieIdentifier)))
{
logger.error(
"NfcReader",
"PN5180 reader-ID kunne ikke laeses.");
return;
}
bool allZero = true;
bool allOnes = true;
for (const uint8_t value :
dieIdentifier)
{
allZero =
allZero &&
value == 0x00;
allOnes =
allOnes &&
value == 0xFF;
}
if (allZero ||
allOnes)
{
logger.error(
"NfcReader",
"PN5180 returnerede et ugyldigt reader-ID.");
return;
}
readerId.reserve(
sizeof(dieIdentifier) *
2);
for (const uint8_t value :
dieIdentifier)
{
if (value < 0x10)
{
readerId += '0';
}
readerId += String(
value,
HEX);
}
readerId.toUpperCase();
}
void Pn5180Reader::setDeviceInfo(
const FilamentGuardDeviceInfo& newDeviceInfo)
{
deviceInfo =
newDeviceInfo;
}
void Pn5180Reader::printVersionInformation()
{
uint8_t productVersion[2] = {};
uint8_t firmwareVersion[2] = {};
uint8_t eepromVersion[2] = {};
const bool productRead =
iso15693Reader.readEEprom(
PRODUCT_VERSION,
productVersion,
sizeof(productVersion));
const bool firmwareRead =
iso15693Reader.readEEprom(
FIRMWARE_VERSION,
firmwareVersion,
sizeof(firmwareVersion));
const bool eepromRead =
iso15693Reader.readEEprom(
EEPROM_VERSION,
eepromVersion,
sizeof(eepromVersion));
if (!productRead ||
!firmwareRead ||
!eepromRead)
{
logger.error(
"NfcReader",
"Kunne ikke laese versionsoplysninger fra PN5180.");
return;
}
const String versionData =
String("{\"readerId\":\"") +
readerId +
"\",\"productVersion\":\"" +
productVersion[1] +
"." +
productVersion[0] +
"\",\"firmwareVersion\":\"" +
firmwareVersion[1] +
"." +
firmwareVersion[0] +
"\",\"eepromVersion\":\"" +
eepromVersion[1] +
"." +
eepromVersion[0] +
"\"}";
logger.info(
"NfcReader",
"PN5180 versionsoplysninger laest.",
versionData);
}
const String& Pn5180Reader::getReaderId() const
{
return readerId;
}
bool Pn5180Reader::scanForTag(
uint8_t* uid,
uint16_t& signalStrength)
{
signalStrength = 0;
if (uid == nullptr)
{
return false;
}
std::memset(
uid,
0,
8);
iso15693Reader.reset();
const bool rfReady =
iso15693Reader.setupRF();
if (rfReady)
{
// Giv det passive tag tid til at blive forsynet efter skiftet
// mellem de to antenners RF-felter.
delay(RfSettleDelayMilliseconds);
}
const ISO15693ErrorCode result =
rfReady
? (performInventory(uid)
? ISO15693_EC_OK
: EC_NO_CARD)
: ISO15693_EC_UNKNOWN_ERROR;
#if FILAMENT_GUARD_NFC_DIAGNOSTICS
if (static_cast<long>(
millis() -
nextDiagnosticAt) >= 0)
{
nextDiagnosticAt =
millis() +
2000;
Serial.print(
"[Debug] [NfcReader] readerId=");
Serial.print(
readerId);
Serial.print(
", rfReady=");
Serial.print(
rfReady
? "true"
: "false");
Serial.print(
", inventoryResult=");
Serial.print(
static_cast<int>(
result));
Serial.print(
", rawUid=");
for (uint8_t index = 0;
index < 8;
++index)
{
if (uid[7 - index] < 0x10)
{
Serial.print('0');
}
Serial.print(
uid[7 - index],
HEX);
if (index < 7)
{
Serial.print(':');
}
}
Serial.println();
}
#endif
if (result != ISO15693_EC_OK ||
!isValidIso15693Uid(uid))
{
iso15693Reader.setRF_off();
return false;
}
uint32_t rfStatus = 0;
if (iso15693Reader.readRegister(
RF_STATUS,
&rfStatus))
{
signalStrength =
static_cast<uint16_t>(
rfStatus &
0x03FF);
}
iso15693Reader.setRF_off();
return true;
}
bool Pn5180Reader::registerTag(
const uint8_t* uid,
const uint8_t uidLength)
{
if (uid == nullptr ||
uidLength != 8 ||
!isValidIso15693Uid(uid) ||
activeProtocol !=
TagProtocol::None)
{
return false;
}
iso15693Reader.reset();
if (!iso15693Reader.setupRF())
{
iso15693Reader.setRF_off();
return false;
}
delay(RfSettleDelayMilliseconds);
const String tagData =
String("{\"readerId\":\"") +
readerId +
"\",\"protocol\":\"ISO15693\"," +
"\"tagUid\":\"" +
formatUid(
uid,
uidLength,
true) +
"\"}";
logger.info(
"NfcReader",
"ISO15693-tag fundet.",
tagData);
if (!dumpIso15693Tag(
uid,
uidLength))
{
iso15693Reader.setRF_off();
return false;
}
rememberUid(
uid,
uidLength,
TagProtocol::Iso15693);
iso15693Reader.setRF_off();
return true;
}
bool Pn5180Reader::isActiveTag(
const uint8_t* uid,
const uint8_t uidLength) const
{
return activeProtocol ==
TagProtocol::Iso15693 &&
isSameUid(
uid,
uidLength);
}
bool Pn5180Reader::hasActiveTag() const
{
return activeProtocol !=
TagProtocol::None;
}
void Pn5180Reader::markTagPresent()
{
missingScanCount = 0;
}
bool Pn5180Reader::markTagMissing()
{
const bool wasActive =
activeProtocol !=
TagProtocol::None;
handleTagMissing();
return wasActive &&
activeProtocol ==
TagProtocol::None;
}
void Pn5180Reader::handleTagMissing()
{
if (activeProtocol ==
TagProtocol::None)
{
return;
}
++missingScanCount;
if (missingScanCount <
RequiredMissingScans)
{
return;
}
const String tagUid =
formatUid(
lastUid,
lastUidLength,
true);
const String tagData =
String("{\"readerId\":\"") +
readerId +
"\",\"tagUid\":\"" +
tagUid +
"\"}";
logger.info(
"NfcReader",
"NFC-tag fjernet.",
tagData);
const String eventJson =
String("{\"reader\":{\"id\":\"") +
readerId +
"\"},\"device\":{" +
"\"deviceId\":\"" +
deviceInfo.deviceId +
"\",\"hostname\":\"" +
deviceInfo.hostname +
"\"},\"tag\":{" +
"\"tagUid\":\"" +
tagUid +
"\"}}";
int responseCode = 0;
String responseBody;
const bool successful =
apiClient.sendTagEvent(
eventJson,
responseCode,
responseBody);
if (successful)
{
logger.info(
"Api",
"Tagfjernelse lagt i API-ko.",
responseBody);
}
else
{
logger.error(
"Api",
"Tagfjernelse kunne ikke laegges i API-ko.",
responseBody);
}
clearLastUid();
}
bool Pn5180Reader::performInventory(
uint8_t* uid)
{
if (uid == nullptr)
{
return false;
}
ISO15693ErrorCode lastResult =
ISO15693_EC_UNKNOWN_ERROR;
for (uint8_t attempt = 0;
attempt < MaxInventoryAttempts;
++attempt)
{
std::memset(
uid,
0,
8);
lastResult =
iso15693Reader.getInventory(
uid);
if (lastResult == ISO15693_EC_OK &&
isValidIso15693Uid(uid))
{
#if FILAMENT_GUARD_NFC_DIAGNOSTICS
if (attempt > 0)
{
Serial.print(
"[Debug] [NfcReader] readerId=");
Serial.print(
readerId);
Serial.print(
", inventory succeeded on attempt ");
Serial.println(
attempt + 1);
}
#endif
return true;
}
if (attempt + 1 < MaxInventoryAttempts)
{
delay(InventoryRetryDelayMilliseconds);
}
}
#if FILAMENT_GUARD_NFC_DIAGNOSTICS
if (lastResult != EC_NO_CARD)
{
Serial.print(
"[Debug] [NfcReader] readerId=");
Serial.print(
readerId);
Serial.print(
", inventory failed after ");
Serial.print(
MaxInventoryAttempts);
Serial.print(
" attempts, lastResult=");
Serial.println(
static_cast<int>(lastResult));
}
#endif
return false;
}
bool Pn5180Reader::readIso15693BlockRange(
const uint8_t* uid,
const uint8_t firstBlock,
const uint8_t blockCount,
uint8_t* blockData,
const uint8_t blockSize)
{
constexpr uint8_t maximumBlocksPerRequest = 8;
constexpr uint16_t maximumResponseLength =
1 +
maximumBlocksPerRequest *
4 +
2;
constexpr uint32_t receiveErrorMask =
(1UL << 16) |
(1UL << 17) |
(1UL << 18);
if (uid == nullptr ||
blockData == nullptr ||
blockCount == 0 ||
blockCount >
maximumBlocksPerRequest ||
blockSize != 4)
{
return false;
}
uint8_t command[12] =
{
0x22,
static_cast<uint8_t>(
blockCount == 1
? 0x20
: 0x23),
0, 0, 0, 0, 0, 0, 0, 0,
firstBlock,
static_cast<uint8_t>(
blockCount - 1)
};
std::memcpy(
command + 2,
uid,
8);
const uint8_t commandLength =
blockCount == 1
? 11
: 12;
if (!iso15693Reader.clearIRQStatus(
0xFFFFFFFF) ||
!iso15693Reader.sendData(
command,
commandLength))
{
return false;
}
const unsigned long startedWaiting =
millis();
uint32_t irqStatus = 0;
do
{
irqStatus = 0;
if (!iso15693Reader.readRegister(
IRQ_STATUS,
&irqStatus))
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
if ((irqStatus &
GENERAL_ERROR_IRQ_STAT) != 0)
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
if ((irqStatus &
RX_IRQ_STAT) != 0)
{
break;
}
delay(1);
}
while (millis() -
startedWaiting <
150);
if ((irqStatus &
RX_IRQ_STAT) == 0 ||
(irqStatus &
RX_SOF_DET_IRQ_STAT) == 0)
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
uint32_t rxStatus = 0;
if (!iso15693Reader.readRegister(
RX_STATUS,
&rxStatus) ||
(rxStatus &
receiveErrorMask) != 0)
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
const uint16_t responseLength =
static_cast<uint16_t>(
rxStatus &
0x01FF);
const uint16_t dataLength =
static_cast<uint16_t>(
blockCount) *
blockSize;
const uint16_t minimumResponseLength =
1 +
dataLength;
if (responseLength <
minimumResponseLength ||
responseLength >
minimumResponseLength +
2 ||
responseLength >
maximumResponseLength)
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
uint8_t response[
maximumResponseLength] = {};
if (!iso15693Reader.readData(
responseLength,
response))
{
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
return false;
}
iso15693Reader.clearIRQStatus(
0xFFFFFFFF);
if ((response[0] &
0x01) != 0)
{
return false;
}
std::memcpy(
blockData,
response + 1,
dataLength);
return true;
}
bool Pn5180Reader::isSameUid(
const uint8_t* uid,
const uint8_t uidLength) const
{
if (uid == nullptr ||
uidLength != lastUidLength)
{
return false;
}
return std::memcmp(
lastUid,
uid,
uidLength) == 0;
}
void Pn5180Reader::rememberUid(
const uint8_t* uid,
const uint8_t uidLength,
const TagProtocol protocol)
{
std::memset(
lastUid,
0,
sizeof(lastUid));
std::memcpy(
lastUid,
uid,
uidLength);
lastUidLength =
uidLength;
activeProtocol =
protocol;
logger.setTagUid(
formatUid(
uid,
uidLength,
protocol ==
TagProtocol::Iso15693));
missingScanCount = 0;
}
void Pn5180Reader::clearLastUid()
{
std::memset(
lastUid,
0,
sizeof(lastUid));
lastUidLength = 0;
activeProtocol =
TagProtocol::None;
logger.setTagUid(
String());
missingScanCount = 0;
}
String Pn5180Reader::formatUid(
const uint8_t* uid,
const uint8_t uidLength,
const bool reverseOrder) const
{
if (uid == nullptr ||
uidLength == 0)
{
return String();
}
String result;
result.reserve(
static_cast<size_t>(
uidLength) *
3 -
1);
for (uint8_t index = 0;
index < uidLength;
++index)
{
const uint8_t uidIndex =
reverseOrder
? uidLength - 1 - index
: index;
if (uid[uidIndex] < 0x10)
{
result += '0';
}
result += String(
uid[uidIndex],
HEX);
if (index <
uidLength - 1)
{
result += ':';
}
}
result.toUpperCase();
return result;
}
bool Pn5180Reader::dumpIso15693Tag(
const uint8_t* uid,
const uint8_t uidLength)
{
constexpr uint8_t blockSize = 4;
constexpr uint8_t numberOfBlocks = 80;
constexpr size_t memorySize =
static_cast<size_t>(
blockSize) *
numberOfBlocks;
uint8_t memory[memorySize] = {};
const String memoryData =
String("{\"blockSizeBytes\":") +
blockSize +
",\"numberOfBlocks\":" +
numberOfBlocks +
",\"memorySizeBytes\":" +
memorySize +
"}";
logger.info(
"NfcReader",
"Laeser ISO15693-hukommelse.",
memoryData);
constexpr uint8_t blocksPerRequest = 8;
for (uint8_t firstBlock = 0;
firstBlock < numberOfBlocks;
firstBlock += blocksPerRequest)
{
uint8_t* destination =
memory +
static_cast<size_t>(
firstBlock) *
blockSize;
if (readIso15693BlockRange(
uid,
firstBlock,
blocksPerRequest,
destination,
blockSize))
{
continue;
}
for (uint8_t offset = 0;
offset < blocksPerRequest;
++offset)
{
const uint8_t blockNumber =
firstBlock +
offset;
if (!readIso15693BlockRange(
uid,
blockNumber,
1,
memory +
static_cast<size_t>(
blockNumber) *
blockSize,
blockSize))
{
const String errorData =
String("{\"blockNumber\":") +
blockNumber +
"}";
logger.error(
"NfcReader",
"ISO15693-blok kunne ikke laeses.",
errorData);
logger.error(
"NfcReader",
"OpenPrintTag-parseren blev ikke startet, fordi hukommelsen ikke kunne laeses komplet.");
return false;
}
}
}
logger.info(
"NfcReader",
"Fortolker OpenPrintTag-data.");
OpenPrintTagData openPrintTagData;
if (!openPrintTagParser.parse(
memory,
memorySize,
openPrintTagData))
{
logger.error(
"NfcReader",
"OpenPrintTag-data kunne ikke fortolkes.");
return false;
}
openPrintTagData.tagUid =
formatUid(
uid,
uidLength,
true);
FilamentGuardTagRead tagRead;
tagRead.readerId =
readerId;
tagRead.device =
deviceInfo;
tagRead.tag =
openPrintTagData;
logTagRead(
tagRead);
sendTagRead(
tagRead);
return true;
}
void Pn5180Reader::logTagRead(
const FilamentGuardTagRead& tagRead) const
{
const String json =
tagReadSerializer.serialize(
tagRead);
logger.info(
"NfcReader",
"OpenPrintTag registreret.",
json);
}
void Pn5180Reader::sendTagRead(
const FilamentGuardTagRead& tagRead) const
{
const String json =
tagReadSerializer.serialize(
tagRead);
int responseCode = 0;
String responseBody;
const bool successful =
apiClient.sendTagRead(
json,
responseCode,
responseBody);
if (successful)
{
logger.info(
"Api",
"Tagregistrering lagt i API-ko.",
responseBody);
}
else
{
logger.error(
"Api",
"Tagregistrering kunne ikke laegges i API-ko.",
responseBody);
}
}