Code the Chess Piece Reader

The chess board will contain the reader and transmitter for the locking system. The software needs to do just a couple of things: read NFC/RFID tags to determine if the right combination of pieces has been scanned, and send a command to the receiver telling it to open or close the drawer.

Make sure you have your RFM69 Feather M0 up and running, using the instructions here, here, and here first. Then, you can use the code shown below by copying and pasting it into a new document in the Arduino IDE and saving the file as chessBoardPuzzleLockTX.ino. Then, upload it to your Feather.

Download: file
//Chess Board Puzzle Lock TX
// by John Park
// Using PN532 NFC Breakout Board on Feather M0 RFM69HCW
// based upon readMifareClassic by Adafruit Industries
//and RadioHead69_addrDemo_TX by AirSpayce

//watches for certain pairs of tags, sends a message

/*************************** Libraries ******************************/
#include <Wire.h>
#include <RH_RF69.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
#include <RHReliableDatagram.h>

/*************************** NFC Defines ******************************/
// If using the breakout or shield with I2C, define just the pins connected
// to the IRQ and reset lines.  Use the values below (2, 3) for the shield!
#define PN532_IRQ   (5)
#define PN532_RESET (6)  // Not connected by default on the NFC Shield

// Or use this line for a breakout or shield with an I2C connection:
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);

/*************************** Radio Defines ******************************/
// Change to 434.0 or other frequency, must match RX's freq!
#define RF69_FREQ 915.0
#define RFM69_CS      8
#define RFM69_INT     3
#define RFM69_RST     4
#define LED           13

// Where to send packets to!
#define DEST_ADDRESS   1
// change addresses for each client board, any number :)
#define MY_ADDRESS     2

// Singleton instance of the radio driver
RH_RF69 rf69(RFM69_CS, RFM69_INT);

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram rf69_manager(rf69, MY_ADDRESS);

int16_t packetnum = 0;  // packet counter, we increment per xmission

/*************************** NFC/RFID Card IDs  ******************************/

char* CARDS[] = {
  "Pawn",
  "Black Rook",
  "Black Knight",
  "Ring North",
  "Ring South"
  } ;

long CIDS[]={
  3125135392,
  3125205536,
  3124812064,
  3561521440,
  3561521184
} ;

//char cardTX[] = { //only used if sending individal card reads
//  'a','b','c','d','e'
//} ;

int cardCount = 1;

long lastCID;

uint32_t firstPiece;  //to check which piece is there
uint32_t secondPiece; //to check which piece is there

/***************************  Setup ******************************/

void setup() {

  Serial.begin(115200);
  //while (!Serial); // wait for serial console to open. remove if not tethered to computer
  
  pinMode(LED, OUTPUT);     
  pinMode (PN532_IRQ, INPUT);
  digitalWrite(PN532_IRQ, HIGH);
  pinMode(RFM69_RST, OUTPUT);
  digitalWrite(RFM69_RST, LOW);

  Serial.println("Chess Puzzle Card Reader");

  nfc.begin();

  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  // Got ok data, print it out!
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
  
  // configure board to read RFID tags
  nfc.SAMConfig();
  
  Serial.println("Waiting for an ISO14443A Card ...");



  Serial.println("\nFeather RFM69 TX...");
  Serial.println();

  // manual reset
  digitalWrite(RFM69_RST, HIGH);
  delay(10);
  digitalWrite(RFM69_RST, LOW);
  delay(10);


  if (!rf69_manager.init()) {
    Serial.println("RFM69 radio init failed");
    while (1);
  }
  Serial.println("RFM69 radio init OK!");
  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module)
  if (!rf69.setFrequency(RF69_FREQ)) {
    Serial.println("setFrequency failed");
  }
 
  // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
  // ishighpowermodule flag set like this:
  rf69.setTxPower(20, true);  // range from 14-20 for power, 2nd arg must be true for 69HCW
 
  // The encryption key has to be the same as the one in the server
  uint8_t key[] = { 0x05, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};

  rf69.setEncryptionKey(key);

  Serial.print("RFM69 radio @");  
  Serial.print((int)RF69_FREQ);  
  Serial.println(" MHz");

}


// Dont put this on the stack:
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
uint8_t data[] = "  OK";


/*************************** Loop ******************************/

void loop() {

  uint8_t success; //store a sucessful card read
  uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
  uint8_t uidLength;                        // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
    
  // Wait for an ISO14443A type cards (Mifare, etc.).  When one is found
  // 'uid' will be populated with the UID, and uidLength will indicate
  // if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  
  if (success) { //any card has been read
    Blink(LED, 50, 1); //blink LED 1 times, 50ms between blinks

    
    // Display some basic information about the card
    //display these values in Serial monitor to generate list of known cards
    Serial.println("\n ------------------------------");
    Serial.println("\n Found an ISO14443A card");
    Serial.print("  UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
    Serial.print("  UID Value: ");
    nfc.PrintHex(uid, uidLength);
    Serial.println("\n ------------------------------");
    

    if(uidLength == 7){ //we've read a card w 7 bytes

        //the next lines take the "uid[]"" array and populate the integer "cardid" value 
        uint32_t cardid = uid[0]; //build the cardid variable from the uid array
        cardid <<= 8;
        cardid |= uid[1];
        cardid <<= 8;
        cardid |= uid[2];  
        cardid <<= 8;
        cardid |= uid[3]; 
        cardid <<= 8;
        cardid |= uid[4];
        cardid <<= 8;
        cardid |= uid[5];
        cardid <<= 8;
        cardid |= uid[6];
        cardid <<= 8;
        cardid |= uid[7];
        
        Serial.print(" cardid: ");
        Serial.println(cardid); 
        digitalWrite(13,HIGH);



        if(cardid!=lastCID){ // enable to prevent mult reads of same object from showing up
          for(int i=0; i<32; i++){ //check against the big list
            if(CIDS[i]==cardid){
              Serial.print("\nPiece ");
              Serial.print(cardCount);
              Serial. print(" is: ");
              Serial.println(CARDS[i]);
              Serial.println("\n ------------------------------");
            }
          }
          firstPiece=lastCID;
          Serial.print("firstPiece: ");
          Serial.println(firstPiece);

          secondPiece=cardid;
          Serial.print("secondPiece: ");
          Serial.println(secondPiece);
          
        }//end it isn't the same card as previous one 

        Serial.println("\n  same piece\n");
        lastCID=cardid;
        delay(500);
        digitalWrite(13,LOW);
        cardCount ++;
    } // end if UID length is 7

      //this is for checking 4 byte card cardid #s, integrate as above if using those cards instead of 7 byte cards
      //if(uidLength == 4){
        //uint32_t cardid = uid[0];
        //cardid <<= 8;
        //cardid |= uid[1];
        //cardid <<= 8;
        //cardid |= uid[2];  
        //cardid <<= 8;
        //cardid |= uid[3]; 
        //the next two lines give us the integer value of the card id to use in the cardid array
        //copy these to make your list
        //Serial.print("cardid: ");
        //Serial.println(cardid); 
        //digitalWrite(13,HIGH);
    //}//end 4 byte card read

        //test the pieces read against each unlock pairing, send the unlock message
        //Pawn and Rook
        if((firstPiece==3125205536 && secondPiece==3125135392)||(firstPiece==3125135392 && secondPiece==3125205536)){ //rook, pawn
          Serial.println("...................SOLVED");
          Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>U N L O C K  I T");
          //send unlock message to the receiver
          //send the  unlock code 'A'
          char radiopacket[20];
          radiopacket[0]='A';
          radiopacket[1]='\0';
          //itoa(packetnum++, radiopacket+13, 10);
          Serial.print("-Sending "); 
          Serial.println(radiopacket[0]);
     
          // Send a message to the DESTINATION!  
          if (rf69_manager.sendtoWait((uint8_t *)radiopacket, strlen(radiopacket), DEST_ADDRESS)) {
          // Now wait for a reply from the server
            uint8_t len = sizeof(buf);
            uint8_t from;   
            if (rf69_manager.recvfromAckTimeout(buf, &len, 2000, &from)) {
              buf[len] = 0; // zero out remaining string
              
              Serial.print("\n\tGot reply from #"); Serial.print(from);
              Serial.print(" [RSSI :");
              Serial.print(rf69.lastRssi());
              Serial.print("] : ");
              Serial.println((char*)buf);     
              Blink(LED, 40, 3); //blink LED 3 times, 40ms between blinks
              delay(100);
            } 

            else {
              Serial.println("No reply, is anyone listening?");
            }
          } 
        
          else {
            Serial.println("Sending failed (no ack)");
          }
        }//end unlock pair
      

      //test the pieces read against each lock pairing, send the LOCK message
      //Reset Ring N/S
      if((firstPiece==3561521440 && secondPiece==3561521184)||(firstPiece==3561521184 && secondPiece==3561521440)){ //rook, pawn
        Serial.println("...................RESET RING");
        Serial.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> L O C K  I T");
        //send unlock message to the receiver
        //send the  unlock code 'A'
        char radiopacket[20];
        radiopacket[0]='B';
        radiopacket[1]='\0';
        //itoa(packetnum++, radiopacket+13, 10);
        Serial.print("-Sending "); 
        Serial.println(radiopacket[0]);
   
        // Send a message to the DESTINATION!  
        if (rf69_manager.sendtoWait((uint8_t *)radiopacket, strlen(radiopacket), DEST_ADDRESS)) {
        // Now wait for a reply from the server
          uint8_t len = sizeof(buf);
          uint8_t from;   
          if (rf69_manager.recvfromAckTimeout(buf, &len, 2000, &from)) {
            buf[len] = 0; // zero out remaining string
            
            Serial.print("\n\tGot reply from #"); Serial.print(from);
            Serial.print(" [RSSI :");
            Serial.print(rf69.lastRssi());
            Serial.print("] : ");
            Serial.println((char*)buf);     
            Blink(LED, 40, 3); //blink LED 3 times, 40ms between blinks
            delay(100);
          } 

          else {
            Serial.println("No reply, is anyone listening?");
          }
        } 
      
        else {
          Serial.println("Sending failed (no ack)");
        }
      }//end lock pair
  }//end successful read 
}//end loop()

void Blink(byte PIN, byte DELAY_MS, byte loops) {
  for (byte i=0; i<loops; i++)  {
    digitalWrite(PIN,HIGH);
    delay(DELAY_MS);
    digitalWrite(PIN,LOW);
    delay(DELAY_MS);
  }
}

After uploading the code, let's test it out. While keeping the Feather connected to your computer, open the Serial Monitor in the Arduino IDE to the current port being used. Set one of your NFC/RFID tags on the reader (near the rectangular antenna coil side of the board) and you'll see some information displayed about the tag.

You'll need to copy and paste the UID Value: # displayed into the code, overwriting the existing values associated with my tags in favor of your own. 

To use a pair of UID Values to unlock the puzzle, copy both values into the firstPiece and secondPiece values in the conditional statement on line 249. Be sure to copy into both sides of the || condition, so that either order of piece placement is accepted.

For example, scan a tag, copy it's UID Value from the Serial Monitor, then update line 57 of the code where the CIDS[] list is with this ID as the first entry, in my case, 3125135392. This will correlate it to "Pawn" as seen in the code on line 49. If you want to put that tag in the white bishop instead, change line 49 to "White Bishop".

For each tag you scan and enter, you may want to mark the tag with a piece of tape, or write on the bag in which they each came with a marker so you can distinguish them later without needing to re-scan them. Once you've inserted the tags into the bases of the chess pieces this won't be a problem!

It's helpful to have a pair of tags for resetting the puzzle and shutting the drawer. The NFC Smart Ring is perfect for this -- just copy the two UID Values from the ring into the CIDS array and drawer lock condition on line 290.
This guide was first published on Aug 25, 2017. It was last updated on Aug 25, 2017. This page (Code the Chess Piece Reader) was last updated on Aug 23, 2017.