From ec2c71b0be08743187ea9a42280459c8de5a19d9 Mon Sep 17 00:00:00 2001 From: Teo-CD Date: Sun, 10 Sep 2023 20:28:27 +0100 Subject: [PATCH] RFID: functional tag reading Add a new class to handle the RFID module and associated data in order to track active tags. Update main with RFID initialization and a minimal usage to check it is working as expected. --- include/RFID.h | 91 ++++++++++++++++++++++++++++++++++++++ src/RFID.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 16 +++++-- 3 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 include/RFID.h create mode 100644 src/RFID.cpp diff --git a/include/RFID.h b/include/RFID.h new file mode 100644 index 0000000..bcec1d8 --- /dev/null +++ b/include/RFID.h @@ -0,0 +1,91 @@ +// +// Created by trotfunky on 10/09/23. +// + +#ifndef JIN_BARBAPAPA_RFID_H +#define JIN_BARBAPAPA_RFID_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Pinout.h" + +/*** + * Linked list containing the uids of the tags and the corresponding IDs. + */ +struct uidNode { + MFRC522Constants::Uid uid{}; + uidNode* next = nullptr; + // IDs should be positive integers < 127 as the sign is used for direction. + int8_t tag_ID = 0; +}; + +class RFID { +public: + RFID() : comData{}, sd(SD) {}; + void init(); + /*** + * Check if any new tags are present or have left. Only one event is reported + * at a time, another call would be needed to get eventual concurrent events. + * This updates the respective lists and returns the ID with the direction + * of the change. + * @return 0 if no change, +ID if new tag detected, -ID if the tag left. + */ + int8_t checkTags(); +private: + /* Used for "encrypted" communication with the tags. */ + static MFRC522Constants::MIFARE_Key defaultKey; + static constexpr int maxTags = 2; + int currentActiveTags = 0; + + /* + * The linked list tracking active tags. + * It is statically allocated so that the memory footprint is constant. + */ + uidNode activeTagsArray[maxTags]; + uidNode* activeTags = nullptr; + uidNode* nextFreeTagSlot = activeTagsArray; + + MFRC522DriverPinSimple driverPin{pin_NFC1_CS}; + MFRC522DriverSPI driverSpi{driverPin}; + MFRC522 mfrc{driverSpi}; + + /* + * Array used to read and write blocks to the tags. Blocks are 16 bytes but + * during reads a two-bytes CRC is added. + */ + byte comData[18]; + + SDClass &sd; + Metro checkGoneTimer{250}; + + /*** + * Add a tag to the static linked list. + * @return The new node, or nullptr if all nodes are allocated. + */ + uidNode* addActiveTag(const MFRC522Constants::Uid &newTagUid); + /*** + * Remove goneNode from the list and optionally update its predecessor. + * @param goneNode Node to remove from the list. + * @param previousNode Node to update, can be nullptr if goneNode is the head. + */ + void removeActiveTag(uidNode* goneNode, uidNode* previousNode); + + /** + * Read block at blockADdr from tagUid and return if the read succeeded or not. + * The read block will be in comData. + * @param tagUid Uid of the tag to be read. + * @param blockAddr Address of the block to be read. + * @return True if read succeeded, false otherwise. + */ + bool readBlock(MFRC522Constants::Uid &tagUid, byte blockAddr); +}; + +#endif //JIN_BARBAPAPA_RFID_H diff --git a/src/RFID.cpp b/src/RFID.cpp new file mode 100644 index 0000000..da42d4e --- /dev/null +++ b/src/RFID.cpp @@ -0,0 +1,116 @@ +// +// Created by Teo-CD on 10/09/23. +// + +#include "RFID.h" + +MFRC522Constants::MIFARE_Key RFID::defaultKey = {0xFF,0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +void RFID::init() { + // Create and set up a static linked list to manage active tags. + for (int i = 0; i < maxTags - 1; i++) { + activeTagsArray[i].next = &activeTagsArray[i+1]; + } + + mfrc.PCD_Init(); +} + +int8_t RFID::checkTags() { + // First check if some tags are gone. Check for new tags later. + if (currentActiveTags && checkGoneTimer.check()) { + uidNode* previousNode = nullptr; + uidNode* node = activeTags; + + while (node) { + byte comSize = sizeof(comData); + + mfrc.uid = node->uid; + mfrc.PICC_WakeupA(comData, &comSize); + + if (mfrc.PICC_Select(&node->uid, node->uid.size) == MFRC522Constants::STATUS_OK) { + // If we get a response : the tag is still here, halt it again and continue. + mfrc.PICC_HaltA(); + previousNode = node; + node = node->next; +// Serial.printf("UID 0x%lX is still present\n", *(uint32_t *)previousNode->uid.uidByte); + } else { +// Serial.printf("UID 0x%lX is gone\n", *(uint32_t *)node->uid.uidByte); + // If we don't, assume the tag is gone and remove it from the list. + removeActiveTag(node, previousNode); + // Return the negative ID to signal it has gone. + // If other tags are gone we can check on the next loop. + return -node->tag_ID; + } + } + } + + // If we still have room, check for new tags and read their serials. + if ( currentActiveTags >= maxTags || + !mfrc.PICC_IsNewCardPresent() || + !mfrc.PICC_ReadCardSerial()) { + return 0; + } + + // Assume we have a new tag. + uidNode* newTag = addActiveTag(mfrc.uid); + // Tag ID is put in a specific block, which can be read after from comData. + if ( !newTag ) { + return 0; + } + // If the read fails, we cannot use this tag anyway so remove it from the list. + if (!readBlock(newTag->uid, 0x11) ) { + removeActiveTag(newTag, nullptr); + return 0; + } + // TODO: ID check ? + newTag->tag_ID = comData[0]; + return newTag->tag_ID; +} + +uidNode* RFID::addActiveTag(const MFRC522Constants::Uid &newTag) { + if (!nextFreeTagSlot) { + return nullptr; + } + uidNode* temp; + temp = nextFreeTagSlot->next; + // Move the top of the free list to the in use list. + nextFreeTagSlot->next = activeTags; + activeTags = nextFreeTagSlot; + activeTags->uid = newTag; + // Set the top of the free list to the next element, which we saved above. + nextFreeTagSlot = temp; + + currentActiveTags++; + return activeTags; +} + +void RFID::removeActiveTag(uidNode* goneNode, uidNode* previousNode) { + // Handle the edge case where we remove the last active tag. + if (previousNode) { + previousNode->next = goneNode->next; + } else { + activeTags = goneNode->next; + } + goneNode->next = nextFreeTagSlot; + nextFreeTagSlot = goneNode; + + currentActiveTags--; +} + +bool RFID::readBlock(MFRC522Constants::Uid &uidToRead, byte blockAddr) { + if (mfrc.PCD_Authenticate( + MFRC522Constants::PICC_Command::PICC_CMD_MF_AUTH_KEY_A, + blockAddr, (&defaultKey),&uidToRead)!= MFRC522Constants::STATUS_OK) { + Serial.println("Failed to authenticate"); + return false; + } else { + byte size = sizeof(comData); + mfrc.MIFARE_Read(blockAddr, comData, &size); +// Serial.printf("Read block : 0x%lX\n", *(uint32_t *)comData); + } + // Needed otherwise no new communications can happen. + mfrc.PCD_StopCrypto1(); + // No need to keep the tag active. + mfrc.PICC_HaltA(); + return true; +} diff --git a/src/main.cpp b/src/main.cpp index 5c71739..677112f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,9 @@ #include -#include -#include - -#include #include "Pinout.h" +#include "RFID.h" + __attribute__((noreturn)) int main() { pinMode(pin_DBG_LED_1, OUTPUT); pinMode(pin_DBG_LED_2, OUTPUT); @@ -41,9 +39,19 @@ __attribute__((noreturn)) int main() { Serial.println("# System is powered up, running set-up."); /* TODO: Setups once module structure is up. */ + RFID rfid; + digitalWrite(pin_NFC1_RST, HIGH); + rfid.init(); /* Main loop */ while (true) { + int8_t tagID; + tagID = rfid.checkTags(); + if (tagID) { + Serial.printf("Check tag result : %d\n", tagID); + } + + delay(100); } }