diff --git a/experimental/devices/mfrc522/commands/low_level.go b/experimental/devices/mfrc522/commands/low_level.go index 2ae79bf..2f773ab 100644 --- a/experimental/devices/mfrc522/commands/low_level.go +++ b/experimental/devices/mfrc522/commands/low_level.go @@ -35,9 +35,9 @@ type AuthStatus byte // NewLowLevelSPI creates and initializes the RFID card reader attached to SPI. // -// spiPort - the SPI device to use. -// resetPin - reset GPIO pin. -// irqPin - irq GPIO pin. +// spiPort - the SPI device to use. +// resetPin - reset GPIO pin. +// irqPin - irq GPIO pin. func NewLowLevelSPI(spiPort spi.Port, resetPin gpio.PinOut, irqPin gpio.PinIn) (*LowLevel, error) { if resetPin == nil { return nil, wrapf("reset pin is not set") diff --git a/experimental/devices/mfrc522/commands/picc.go b/experimental/devices/mfrc522/commands/picc.go index 3d2f9ce..6e60f63 100644 --- a/experimental/devices/mfrc522/commands/picc.go +++ b/experimental/devices/mfrc522/commands/picc.go @@ -17,6 +17,7 @@ const ( PICC_REQIDL = 0x26 PICC_REQALL = 0x52 PICC_ANTICOLL = 0x93 + PICC_ANTICOLL2 = 0x95 PICC_SElECTTAG = 0x93 PICC_AUTHENT1A = 0x60 PICC_AUTHENT1B = 0x61 diff --git a/experimental/devices/mfrc522/example_test.go b/experimental/devices/mfrc522/example_test.go index 4fd65d2..f6ecedf 100644 --- a/experimental/devices/mfrc522/example_test.go +++ b/experimental/devices/mfrc522/example_test.go @@ -23,7 +23,7 @@ func Example() { log.Fatal(err) } - // Using SPI as an example. See package ./spi/spireg for more details. + // Using SPI as an example. See package "periph.io/x/periph/conn/spi/spireg" for more details. p, err := spireg.Open("") if err != nil { log.Fatal(err) @@ -102,3 +102,75 @@ func Example() { } } } + +func ExampleDev_ReadUID() { + // Make sure periph is initialized. + if _, err := host.Init(); err != nil { + log.Fatal(err) + } + + // Using SPI as an example. See package "periph.io/x/periph/conn/spi/spireg" for more details. + p, err := spireg.Open("") + if err != nil { + log.Fatal(err) + } + defer p.Close() + + rfid, err := mfrc522.NewSPI(p, rpi.P1_22, rpi.P1_18) + if err != nil { + log.Fatal(err) + } + + // Idling device on exit. + defer rfid.Halt() + + // Setting the antenna signal strength. + rfid.SetAntennaGain(5) + + timedOut := false + cb := make(chan []byte) + timer := time.NewTimer(10 * time.Second) + + // Stopping timer, flagging reader thread as timed out + defer func() { + timer.Stop() + timedOut = true + close(cb) + }() + + go func() { + log.Printf("Started %s", rfid.String()) + + for { + // Trying to read card UID. + uid, err := rfid.ReadUID(10 * time.Second) + + // If main thread timed out just exiting. + if timedOut { + return + } + + // Some devices tend to send wrong data while RFID chip is already detected + // but still "too far" from a receiver. + // Especially some cheap CN clones which you can find on GearBest, AliExpress, etc. + // This will suppress such errors. + if err != nil { + continue + } + + cb <- uid + return + } + }() + + for { + select { + case <-timer.C: + log.Fatal("Didn't receive device data") + return + case data := <-cb: + log.Println("UID:", hex.EncodeToString(data)) + return + } + } +} diff --git a/experimental/devices/mfrc522/mfrc522.go b/experimental/devices/mfrc522/mfrc522.go index e5b7dcc..683cbb5 100644 --- a/experimental/devices/mfrc522/mfrc522.go +++ b/experimental/devices/mfrc522/mfrc522.go @@ -123,6 +123,20 @@ func (r *Dev) SetAntennaGain(gain int) error { return nil } +// ReadUID reads the card UID with IRQ event timeout. +// +// timeout the operation timeout +func (r *Dev) ReadUID(timeout time.Duration) (uid []byte, err error) { + r.beforeCall() + defer func() { + r.afterCall() + if err == nil { + err = r.LowLevel.StopCrypto() + } + }() + return r.selectCard(timeout) +} + // ReadCard reads the card sector/block with IRQ event timeout. // // timeout the operation timeout @@ -303,6 +317,35 @@ func (r *Dev) antiColl() ([]byte, error) { return backData, nil } +// antiColl2 performs additional anticollision check for UIDs with more than 4 bytes. +func (r *Dev) antiColl2() ([]byte, error) { + if err := r.LowLevel.DevWrite(commands.BitFramingReg, 0x00); err != nil { + return nil, err + } + + serial := []byte{commands.PICC_ANTICOLL2, 0x20} + check := byte(0) + + backData, _, err := r.LowLevel.CardWrite(commands.PCD_TRANSCEIVE, serial) + if err != nil { + return nil, err + } + + if len(backData) != 5 { + return nil, wrapf("Anticoll2 error, response is %d bytes-long, expected 5", len(backData)) + } + + for i := 0; i < 4; i++ { + check ^= backData[i] + } + + if check != backData[4] { + return nil, wrapf("Anticoll2 error, check failed") + } + + return backData, nil +} + // selectTag selects the FOB device by device UUID. func (r *Dev) selectTag(serial []byte) (byte, error) { dataBuf := make([]byte, len(serial)+2) @@ -355,6 +398,17 @@ func (r *Dev) selectCard(timeout time.Duration) ([]byte, error) { if _, err := r.selectTag(uuid); err != nil { return nil, err } + + if uuid[0] == 0x88 { // Incomplete UID + // Get remaining bytes + uuidEnd, err := r.antiColl2() + if err != nil { + return nil, err + } + + uuid = uuid[1 : len(uuid)-1] + uuid = append(uuid, uuidEnd[:len(uuidEnd)-1]...) + } return uuid, nil }