system: device provider: sensor/rc522 introduced in v0.1 internalPull presence Arch restricted: arm GPIO: SCI

rc522 or rf522 are cheap NFC readers.

go-home provides ability to read certain block of a data (16 bytes) to compare with your pre-defined “pass-codes” to determine which user was authorized.

Example pinout

rc522 Pin Line RPi Pin
0 SDA 24
1 SCK 23
2 MOSI 19
3 MISO 21
4 IRQ 11
5 GND 6
6 RST 13
7 +3.3V 1

Configuration options

Param Required Type Default Description
bus yes int 0 Bus ID to use
device yes int 0 Device ID to use. Final device address is /dev/spidev<bus>.<device>
reset yes int 13 Physical number of the reset (RST) pin
irq yes int 13 Physical number of the IRQ pin
antennaGain int 4 Antenna gain (0-7)
sector yes int 1 Sector number where the key is located
block yes int 0 Block number where the key is located
key yes string Byte-string representing encryption key
users yes map[string]string List of key-value pairs with users and corresponding keys

Supported properties

Property Type Description
on bool Flag indicating whether user was successfully authorized
user string Name of the users from users configuration option


system: device
provider: sensor/rc522
name: nfc
bus: 0
device: 0
reset: 13
irq: 11
sector: 1
block: 0
  vlad: "407e3e663d556d5b587b4c527741337d"
key: "030e0f5c4123"
  name: worker-static-1

Here 030e0f5c4123 is a byte-string, equals to [6]byte{3, 14, 15, 92, 65, 35} from example below. 407e3e663d556d5b587b4c527741337d is a byte-string equals to @~>f=Um[X{LRwA3}.

Configuring keys

To write your own secret you can use the following snippet.

Data is actually a [16]byte, for simplicity string is used.

package main

import (

type fakeLogger struct {

func (l *fakeLogger) Format(e *logrus.Entry) ([]byte, error) {
    return nil, nil

func main() {
    currentAccessKey := [6]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
    newAccessKey := [6]byte{3, 14, 15, 92, 65, 35}
    sector := 1
    block := 0
    mySecret := "@~>f=Um[X{LRwA3}"

    rfid, err := rf522.MakeRFID(0, 0, 1000000, 13, 11)
    if err != nil {

    data := waitRead(rfid, sector, block, currentAccessKey)
    fmt.Printf("RFID sector %d, block %d : %v (str: %s)", sector, block, data, string(data))

    conv := [16]byte{}
    for i, v := range mySecret {
        conv[i] = byte(v)

    err = rfid.WriteBlock(byte(commands.PICC_AUTHENT1B), sector, block, conv, currentAccessKey[:])

    if err != nil {

    err = rfid.WriteSectorTrail(commands.PICC_AUTHENT1A, sector, currentAccessKey, newAccessKey, &rf522.BlocksAccess{
        B0: rf522.RAB_WB_IB_DAB,
        B1: rf522.RB_WB_IN_DN,
        B2: rf522.AnyKeyRWID,
        B3: rf522.KeyA_RN_WN_BITS_RAB_WN_KeyB_RN_WN,
    }, currentAccessKey[:])

    if err != nil {

    data = waitRead(rfid, sector, block, newAccessKey)
    fmt.Printf("RFID sector %d, block %d : %v (str: %s)", sector, block, data, string(data))

    if mySecret != string(data) {
        panic("Failed to update")

func waitRead(rfid *rf522.RFID, sector int, block int, key [6]byte) []byte {
    for {
        data, err := rfid.ReadCard(byte(commands.PICC_AUTHENT1B), sector, block, key[:])
        if err != nil {

        return data