// SPDX-License-Identifier: GPL-3.0-or-later // // Copyright (c) 2019-2023 plan44.ch / Lukas Zeller, Zurich, Switzerland // // Author: Lukas Zeller <luz@plan44.ch> // // Based on code by Miguel Balboa (circuitito.com), Jan, 2012, // "RFID.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT." // which was based on code by Dr.Leong (WWW.B2CQSHOP.COM) // and was modified by Paul Kourany to run on Spark Core with added support for Software SPI, Mar, 2014. // // This file is part of p44utils. // // p44utils is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // p44utils is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with p44utils. If not, see <http://www.gnu.org/licenses/>. // #ifndef rfid_hpp #define rfid_hpp #include "p44utils_main.hpp" #ifndef ENABLE_RFID #define ENABLE_RFID 1 #endif #if ENABLE_RFID #include <stdio.h> #include "digitalio.hpp" #include "spi.hpp" #define DEFAULT_COMMAND_TIMEOUT (250*MilliSecond) using namespace std; namespace p44 { class RFIDError : public Error { public: // Errors typedef enum { OK, ChipTimeout, ///< timeout ChipErr, ///< chip error UnknownCmd, ///< unknown command BadAnswer, ///< bad answer (e.g. wrong number of bits, checksum error) IRQTimeout, ///< IRQ timeout (irqHandler() not called soon enough) } ErrorCodes; static const char *domain() { return "RFID"; } virtual const char *getErrorDomain() const { return RFIDError::domain(); }; RFIDError(ErrorCodes aError) : Error(ErrorCode(aError)) {}; }; class RFID522 : public P44LoggingObj { public: /// @param aReaderIndex index of reader to select, RFID522::Deselect = none selected typedef boost::function<void (int aReaderIndex)> SelectCB; static const int Deselect = -1; ///< pseudo-index to deselect all readers /// execPICCCmd result callback typedef boost::function<void (ErrorPtr aErr, uint16_t aResultBits, const string aResult)> ExecResultCB; private: SPIDevicePtr mSpiDev; int mReaderIndex; SelectCB mReaderSelectFunc; // execPICCCmd state ExecResultCB mExecResultCB; uint8_t mCmd; ///< the command being executed uint8_t mIrqEn; ///< enabled IRQs uint8_t mWaitIrq; ///< IRQs we are waiting for to terminate execPICCCmd uint16_t mChipTimer; ///< the chip timer (preload) value to set bool mUseIrqWatchdog; MLTicket mIrqWatchdog; MLMicroSeconds mCmdStart; MLMicroSeconds mCmdTimeout; public: /// create RFID522 reader instance /// @param aSPIGenericDev a generic SPI device for the bus this reader is connected to /// @param aReaderIndex the selection address of this reader /// @param aReaderSelectFunc will be called to select this particular reader by aSelectAddress /// @param aChipTimer the chip timer (preload) value to set, 0 means using default /// @param aUserIrqWatchdog if set, the IRQ watchdog is used /// @param aCmdTimeout command timeout RFID522( SPIDevicePtr aSPIGenericDev, int aReaderIndex, SelectCB aReaderSelectFunc, uint16_t aChipTimer = 0, bool aUseIrqWatchdog = false, MLMicroSeconds aCmdTimeout = DEFAULT_COMMAND_TIMEOUT ); virtual ~RFID522(); /// @return the object type (used for context descriptions such as logging context) virtual string contextType() const P44_OVERRIDE { return "RFID522"; }; /// @return the object type (used for context descriptions such as logging context) virtual string contextId() const P44_OVERRIDE; /// get this reader's index int getReaderIndex() { return mReaderIndex; }; /// init this reader /// @param aRegValPairs byte pairs of register addresses/values for extra initialisation /// @return true if version register did return a sensible value, false when no or malfunctioning reader is connected bool init(const string aRegValPairs); void reset(); /// must be called when external IRQ line (possibly common to multiple readers) gets active /// @return true if reader still waits for an interrupt bool irqHandler(); /// check for Type A (MiFare) card /// @param aStatusCB returns ok when there is a card, Error otherwise /// @param aWait - repeat command until there is an answer from a card (ok or error), do not timeout void probeTypeA(StatusCB aStatusCB, bool aWait = false); /// run anticollision procedure and return card nUID /// @param aResultCB returns card nUID as response or error /// @param aStoreNUID if set, the winning card's nUID is stored for selecting later void antiCollision(ExecResultCB aResultCB, bool aStoreNUID = false); /// switch the energy field void energyField(bool aEnable); /// abort running command void returnToIdle(); /// continue transceiving (e.g. probing) void continueTransceiving(); private: /// write single byte to a register void writeReg(uint8_t aReg, uint8_t aVal); /// write multiple bytes to FIFO data register void writeFIFO(const uint8_t* aData, size_t aNumBytes); /// read single byte from a register uint8_t readReg(uint8_t aReg); /// read multiple bytes from FIFO data register void readFIFO(uint8_t* aData, size_t aNumBytes); /// set some bits in a register (read-modify-write) void setRegBits(uint8_t reg, uint8_t mask); /// clear some bits in a register (read-modify-write) void clrRegBits(uint8_t reg, uint8_t mask); /// set the timeout timer void setTimer(uint16_t aTimerReload); /// execute PICC command //void execPICCCmd(uint8_t aCmd, uint8_t *aTxDataP, uint8_t aTxBytes, uint8_t *aRxDataP, uint16_t &aRxBits); void execPICCCmd(uint8_t aCmd, const string aTxData, uint8_t aBitFraming, ExecResultCB aResultCB); // execPICCCmd helpers void execResult(ErrorPtr aErr, uint16_t aResultBits = 0, const string aResult = ""); void commandTimeout(); void irqTimeout(MLTimer &aTimer); /// Search for cards in field /// @param aReqCmd - REQA, REQB, WUPA, WUPB /// @param aWait - repeat command until there is an answer from a card (ok or error), do not timeout /// @param aStatusCB called to report card (ATQx received: ok, error otherwise) void requestPICC(uint8_t aReqCmd, bool aWait, StatusCB aStatusCB); // requestPICC helper void requestResponse(uint8_t aReqCmd, StatusCB aStatusCB, bool aWait, ErrorPtr aErr, uint16_t aResultBits, const string aResult); // anticoll and readCardSerial helper void anticollResponse(ExecResultCB aResultCB, bool aStoreNUID, ErrorPtr aErr, uint16_t aResultBits, const string aResult); void calculateCRC(uint8_t *pIndata, uint8_t len, uint8_t *pOutData); /* uint8_t auth(uint8_t authMode, uint8_t BlockAddr, uint8_t *Sectorkey, uint8_t *serNum); uint8_t read(uint8_t blockAddr, uint8_t *recvData); uint8_t write(uint8_t blockAddr, uint8_t *writeData); void halt(); */ uint8_t serNum[5]; // the serial number. uint8_t AserNum[5]; // the serial number of the current section. }; typedef boost::intrusive_ptr<RFID522> RFID522Ptr; } // namespace p44 #endif // ENABLE_RFID #endif /* rfid_hpp */