hybridgroup.gobot/drivers/common/mfrc522/mfrc522_pcd.go

394 lines
11 KiB
Go

package mfrc522
import (
"fmt"
"time"
)
// PCD: proximity coupling device (reader unit)
// PICC: proximity integrated circuit card (the card or chip)
const (
pcdDebug = false
initTime = 50 * time.Millisecond
// at least 5 ms are needed after switch on, see AN10834
antennaOnTime = 10 * time.Millisecond
)
type busConnection interface {
ReadByteData(reg byte) (byte, error)
WriteByteData(reg byte, data byte) error
}
var versions = map[uint8]string{
0x12: "Counterfeit", 0x88: "FM17522", 0x89: "FM17522E",
0x90: "MFRC522 0.0", 0x91: "MFRC522 1.0", 0x92: "MFRC522 2.0", 0xB2: "FM17522 1",
}
// MFRC522Common is the Gobot Driver for MFRC522 RFID.
// datasheet:
// https://www.nxp.com/docs/en/data-sheet/MFRC522.pdf
//
// reference implementations:
// * https://github.com/OSSLibraries/ArduinoRegMFRC522v2
// * https://github.com/jdevelop/golang-rpi-extras
// * https://github.com/pimylifeup/MFRC522-python
// * https://periph.io/device/mf-rc522/
type MFRC522Common struct {
connection busConnection
firstCardAccess bool
}
// NewMFRC522Common creates a new Gobot Driver for MFRC522 RFID with specified bus connection
// The device supports SPI, I2C and UART (not implemented yet at gobot system level).
//
// Params:
//
// c BusConnection - the bus connection to use with this driver
func NewMFRC522Common() *MFRC522Common {
d := &MFRC522Common{}
return d
}
// Initialize sets the connection and initializes the driver.
func (d *MFRC522Common) Initialize(c busConnection) error {
d.connection = c
if err := d.softReset(); err != nil {
return err
}
initSequence := [][]byte{
{regTxMode, rxtxModeRegReset},
{regRxMode, rxtxModeRegReset},
{regModWidth, modWidthRegReset},
{regTMode, tModeRegTAutoBit | 0x0F}, // timer starts automatically at the end of the transmission
{regTPrescaler, 0xFF},
{regTReloadL, tReloadRegValue25ms & 0xFF},
{regTReloadH, tReloadRegValue25ms >> 8},
{regTxASK, txASKRegForce100ASKBit},
{regMode, modeRegTxWaitRFBit | modeRegPolMFinBit | modeRegCRCPreset6363},
}
for _, init := range initSequence {
if err := d.writeByteData(init[0], init[1]); err != nil {
return err
}
}
if err := d.switchAntenna(true); err != nil {
return err
}
if err := d.setAntennaGain(rfcCfgRegRxGain38dB); err != nil {
return err
}
return nil
}
// PrintReaderVersion gets and prints the reader (pcd) version.
func (d *MFRC522Common) PrintReaderVersion() error {
version, err := d.getVersion()
if err != nil {
return err
}
fmt.Printf("PCD version: '%s' (0x%X)\n", versions[version], version)
return nil
}
func (d *MFRC522Common) getVersion() (uint8, error) {
return d.readByteData(regVersion)
}
func (d *MFRC522Common) switchAntenna(targetState bool) error {
val, err := d.readByteData(regTxControl)
if err != nil {
return err
}
maskForOn := uint8(txControlRegTx2RFEn1outputBit | txControlRegTx1RFEn1outputBit)
currentState := val&maskForOn == maskForOn
if targetState == currentState {
return nil
}
if targetState {
val = val | maskForOn
} else {
val = val & ^maskForOn
}
if err := d.writeByteData(regTxControl, val); err != nil {
return err
}
if targetState {
time.Sleep(antennaOnTime)
}
return nil
}
func (d *MFRC522Common) setAntennaGain(val uint8) error {
return d.writeByteData(regRFCfg, val)
}
func (d *MFRC522Common) softReset() error {
if err := d.writeByteData(regCommand, commandRegSoftReset); err != nil {
return err
}
// The datasheet does not mention how long the SoftReset command takes to complete. According to section 8.8.2 of the
// datasheet the oscillator start-up time is the start up time of the crystal + 37.74 us.
// TODO: this can be done better by wait until the power down bit is cleared
time.Sleep(initTime)
val, err := d.readByteData(regCommand)
if err != nil {
return err
}
if val&commandRegPowerDownBit > 1 {
return fmt.Errorf("initialization takes longer than %s", initTime)
}
return nil
}
func (d *MFRC522Common) stopCrypto1() error {
return d.clearRegisterBitMask(regStatus2, status2RegMFCrypto1OnBit)
}
func (d *MFRC522Common) communicateWithPICC(command uint8, sendData []byte, backData []byte, txLastBits uint8,
checkCRC bool,
) error {
irqEn := 0x00
waitIRq := uint8(0x00)
switch command {
case commandRegMFAuthent:
irqEn = comIEnRegIdleIEnBit | comIEnRegErrIEnBit
waitIRq = comIrqRegIdleIRqBit
case commandRegTransceive:
irqEn = comIEnRegTimerIEnBit | comIEnRegErrIEnBit | comIEnRegLoAlertIEnBit
irqEn = irqEn | comIEnRegIdleIEnBit | comIEnRegRxIEnBit | comIEnRegTxIEnBit
waitIRq = uint8(comIrqRegIdleIRqBit | comIrqRegRxIRqBit)
}
// TODO: this is not used at the moment (propagation of IRQ pin)
//nolint:gosec // TODO: fix later
if err := d.writeByteData(regComIEn, uint8(irqEn|comIEnRegIRqInv)); err != nil {
return err
}
if err := d.writeByteData(regComIrq, comIrqRegClearAll); err != nil {
return err
}
if err := d.writeByteData(regFIFOLevel, fifoLevelRegFlushBufferBit); err != nil {
return err
}
// stop any active command
if err := d.writeByteData(regCommand, commandRegIdle); err != nil {
return err
}
// prepare and start communication
if err := d.writeFifo(sendData); err != nil {
return err
}
if err := d.writeByteData(regBitFraming, txLastBits); err != nil {
return err
}
if err := d.writeByteData(regCommand, command); err != nil {
return err
}
if command == commandRegTransceive {
if err := d.setRegisterBitMask(regBitFraming, bitFramingRegStartSendBit); err != nil {
return err
}
}
// Wait for the command to complete. On initialization the TAuto flag in TMode register is set. This means the timer
// automatically starts when the PCD stops transmitting.
const maxTries = 5
i := 0
for ; i < maxTries; i++ {
irqs, err := d.readByteData(regComIrq)
if err != nil {
return err
}
if irqs&waitIRq > 0 {
// One of the interrupts that signal success has been set.
break
}
if irqs&comIrqRegTimerIRqBit == comIrqRegTimerIRqBit {
return fmt.Errorf("the timer interrupt occurred")
}
time.Sleep(time.Millisecond)
}
if err := d.clearRegisterBitMask(regBitFraming, bitFramingRegStartSendBit); err != nil {
return err
}
if i >= maxTries {
return fmt.Errorf("no data available after %d tries", maxTries)
}
errorRegValue, err := d.readByteData(regError)
if err != nil {
return err
}
// stop if any errors except collisions were detected.
if err := d.getFirstError(errorRegValue &^ errorRegCollErrBit); err != nil {
return err
}
backLen := len(backData)
var rxLastBits uint8
if backLen > 0 {
rxLastBits, err = d.readFifo(backData)
if err != nil {
return err
}
if pcdDebug {
fmt.Printf("rxLastBits: 0x%02x\n", rxLastBits)
}
}
if err := d.getFirstError(errorRegValue & errorRegCollErrBit); err != nil {
return err
}
if backLen > 2 && checkCRC {
// the last 2 bytes of data contains the CRC
if backLen == 3 && rxLastBits == 0x04 {
return fmt.Errorf("CRC: MIFARE Classic NAK is not OK")
}
if backLen < 4 || backLen == 4 && rxLastBits != 0 {
return fmt.Errorf("CRC: at least the 2 byte CRCRegA value and all 8 bits of the last byte must be received")
}
crcResult := []byte{0x00, 0x00}
crcData := backData[:backLen-2]
dataCrc := backData[backLen-2:]
err := d.calculateCRC(crcData, crcResult)
if err != nil {
return err
}
if dataCrc[0] != crcResult[0] || dataCrc[1] != crcResult[1] {
return fmt.Errorf("CRC: values not match %v - %v", crcResult, dataCrc)
}
}
return nil
}
// 16 bit CRC will be calculated for the given data
func (d *MFRC522Common) calculateCRC(data []byte, result []byte) error {
// Stop any active command.
if err := d.writeByteData(regCommand, commandRegIdle); err != nil {
return err
}
if err := d.writeByteData(regDivIrq, divIrqRegCRCIRqBit); err != nil {
return err
}
if err := d.writeByteData(regFIFOLevel, fifoLevelRegFlushBufferBit); err != nil {
return err
}
if err := d.writeFifo(data); err != nil {
return err
}
if err := d.writeByteData(regCommand, commandRegCalcCRC); err != nil {
return err
}
const maxTries = 3
for i := 0; i < maxTries; i++ {
irqs, err := d.readByteData(regDivIrq)
if err != nil {
return err
}
if irqs&divIrqRegCRCIRqBit == divIrqRegCRCIRqBit {
if err := d.writeByteData(regCommand, commandRegIdle); err != nil {
return err
}
result[0], err = d.readByteData(regCRCResultL)
if err != nil {
return err
}
result[1], err = d.readByteData(regCRCResultH)
if err != nil {
return err
}
return nil
}
time.Sleep(time.Millisecond)
}
return fmt.Errorf("no CRC available after %d tries", maxTries)
}
func (d *MFRC522Common) writeFifo(fifoData []byte) error {
// the register command is always the same, the pointer in FIFO is incremented automatically after each write
for _, b := range fifoData {
if err := d.writeByteData(regFIFOData, b); err != nil {
return err
}
}
return nil
}
func (d *MFRC522Common) readFifo(backData []byte) (uint8, error) {
n, err := d.readByteData(regFIFOLevel) // Number of bytes in the FIFO
if err != nil {
return 0, err
}
//nolint:gosec // TODO: fix later
if n > uint8(len(backData)) {
return 0, fmt.Errorf("more data in FIFO (%d) than expected (%d)", n, len(backData))
}
//nolint:gosec // TODO: fix later
if n < uint8(len(backData)) {
return 0, fmt.Errorf("less data in FIFO (%d) than expected (%d)", n, len(backData))
}
// the register command is always the same, the pointer in FIFO is incremented automatically after each read
for i := 0; i < int(n); i++ {
byteVal, err := d.readByteData(regFIFOData)
if err != nil {
return 0, err
}
backData[i] = byteVal
}
rxLastBits, err := d.readByteData(regControl)
if err != nil {
return 0, err
}
return rxLastBits & controlRegRxLastBits, nil
}
func (d *MFRC522Common) getFirstError(errorRegValue uint8) error {
if errorRegValue == 0 {
return nil
}
if errorRegValue&errorRegProtocolErrBit == errorRegProtocolErrBit {
return fmt.Errorf("a protocol error occurred")
}
if errorRegValue&errorRegParityErrBit == errorRegParityErrBit {
return fmt.Errorf("a parity error occurred")
}
if errorRegValue&errorRegCRCErrBit == errorRegCRCErrBit {
return fmt.Errorf("a CRC error occurred")
}
if errorRegValue&errorRegCollErrBit == errorRegCollErrBit {
return fmt.Errorf("a collision error occurred")
}
if errorRegValue&errorRegBufferOvflBit == errorRegBufferOvflBit {
return fmt.Errorf("a buffer overflow error occurred")
}
if errorRegValue&errorRegTempErrBit == errorRegTempErrBit {
return fmt.Errorf("a temperature error occurred")
}
if errorRegValue&errorRegWrErrBit == errorRegWrErrBit {
return fmt.Errorf("a temperature error occurred")
}
return fmt.Errorf("an unknown error occurred")
}