Issues with PN532 shield and Keybord.h library. Need help!

Hey folks,
I have a project where I would like to use the PN532 breakout board and an Arduino Leonardo to send keyboard commands to a laptop. The idea is to have different RFID cards trigger videos from a media player using the mapped keyboard commands. I have done this many times with just pushbutton switches and using the Keyboard.h library works fine in those applications.

I have connected my PN532 to the Leonardo and used the test sketch using the Adafruit_PN532.h library and the I2C connections. This works great and I can verify the cards on the serial monitor...life is good. Once I add the Keyboard.h library and some simple code to Keyboard.press ("A") based on a Card ID, the loop stops and the sketch never gets off the starting blocks. It's like everything comes to a stop. Where did I go wrong?

/**************************************************************************/
#include <Keyboard.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>


// If using the breakout with SPI, define the pins for SPI communication.
//#define PN532_SCK  (2)
//#define PN532_MOSI (3)
//#define PN532_SS   (4)
//#define PN532_MISO (5)

// 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 (3)  // Not connected by default on the NFC Shield

// Uncomment just _one_ line below depending on how your breakout or shield
// is connected to the Arduino:

// Use this line for a breakout with a SPI connection:
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);

// Use this line for a breakout with a hardware SPI connection.  Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins.  On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12.  The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);

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

void setup(void) {
  Serial.begin(115200);
  while (!Serial) delay(10); // for Leonardo/Micro/Zero

  Serial.println("Hello!");

  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 ...");
}


void loop(void) {
  uint8_t success;
  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) {
    // Display some basic information about the card
    Serial.println("Found an ISO14443A card");
    Serial.print("  UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
    Serial.print("  UID Value: ");
    nfc.PrintHex(uid, uidLength);
   
    if (uidLength == 4)
    {
      // We probably have a Mifare Classic card ...
      uint32_t cardid = uid[0];
      cardid <<= 8;
      cardid |= uid[1];
      cardid <<= 8;
      cardid |= uid[2]; 
      cardid <<= 8;
      cardid |= uid[3];
      Serial.print("Seems to be a Mifare Classic card #");
      Serial.println(cardid);       
    Keyboard.begin(); 
     if (cardid == 1839898475)
      Keyboard.press("A");
       delay(100);
      Keyboard.release("A");
       delay(2000);
    Keyboard.end();     
     Serial.println("");
  }
}
}

Looks like you forgot to call Keyboard.begin();

EDIT: Oops... My mistake. You call it FOR EACH CARD READ.

WARNING: C++ is not like Python. It doesn't care about indentation. Your code means:

    Keyboard.begin(); 
    if (cardid == 1839898475)
    {
      Keyboard.press("A");
    }
    delay(100);
    Keyboard.release("A");
    delay(2000);
    Keyboard.end(); 

That's going to call Keyboard.begin();, Keyboard.release("A"); and Keyboard.end(); for each card read, regardless of ID.

Hey John,

Thanks for the advice. I actually noodled around and got this working by adding the Keyboard.begin at the beginning of the void loop. This works just fine and I was able to map the card ID's to the keyboard commands on the media player. Works great. Just some test code for now.

However, this is just part of a grand design to create a wheel of fortune where each segment (10-pie shaped segments) will have an RFID card that will trigger a different video when read by the reader. The reader (PN532) will be installed at the top of the wheel so when the wheel stops it will send out a keyboard command based on the card ID on that segment. This is tricky because as the wheel spins the PN532 will be reading cards very fast and we don't want false triggers. We will also want the PN532 to trigger the media player with one command and not a continuous series of keyboard commands. Continuous commands would keep triggering the media player and restarting the video over and over.

I haven't figure out how to (A) keep the PN532 from triggering the media player as the wheel spins. Needs to trigger only when stopped (maybe for 2 seconds). And (B) how to keep the keyboard command from constantly triggering the media player once the wheel has stopped. Figure each video will be about 60 seconds.

The test code for reading the cards and keyboard trigger commands is below. I haven't figured out the rest...any help would be appreciated.

#include <Keyboard.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>


// If using the breakout with SPI, define the pins for SPI communication.
//#define PN532_SCK  (2)
//#define PN532_MOSI (3)
//#define PN532_SS   (4)
//#define PN532_MISO (5)

// 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   (6)
#define PN532_RESET (3)  // Not connected by default on the NFC Shield

// Uncomment just _one_ line below depending on how your breakout or shield
// is connected to the Arduino:

// Use this line for a breakout with a SPI connection:
//Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);

// Use this line for a breakout with a hardware SPI connection.  Note that
// the PN532 SCK, MOSI, and MISO pins need to be connected to the Arduino's
// hardware SPI SCK, MOSI, and MISO pins.  On an Arduino Uno these are
// SCK = 13, MOSI = 11, MISO = 12.  The SS line can be any digital IO pin.
//Adafruit_PN532 nfc(PN532_SS);

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

void setup(void) {
  Serial.begin(115200);
  while (!Serial) delay(10); // for Leonardo/Micro/Zero

  Serial.println("Hello!");

  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 ...");
}


void loop(void) {
  Keyboard.begin();
  uint8_t success;
  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) {
    // Display some basic information about the card
    Serial.println("Found an ISO14443A card");
    Serial.print("  UID Length: ");Serial.print(uidLength, DEC);Serial.println(" bytes");
    Serial.print("  UID Value: ");
    nfc.PrintHex(uid, uidLength);
    
    if (uidLength == 4)
    {
      // We probably have a Mifare Classic card ... 
      uint32_t cardid = uid[0];
      cardid <<= 8;
      cardid |= uid[1];
      cardid <<= 8;
      cardid |= uid[2];  
      cardid <<= 8;
      cardid |= uid[3]; 
      Serial.print("Seems to be a Mifare Classic card #");
      Serial.println(cardid);        
     if (cardid == 1839898475)
      Keyboard.write('A');
       delay(200);
     if (cardid == 3335073343)
      Keyboard.write('B');
       delay(200); 
    if (cardid == 2260681791)
      Keyboard.write('C');
       delay(200); 
    if (cardid == 3329626431)
      Keyboard.write('D');
       delay(200);
    if (cardid == 1186344511)
      Keyboard.write('E');
       delay(200);     
    Keyboard.end();      
     Serial.println("");
  }
}
}

I would start by just displaying every ID seen on Serial Monitor. Turn on the timestamp option. Now you can see how often you get ID's while spinning and if it repeats once stopped. Then you can design your code to detect when the wheel has stopped and pick the right code.

Thanks again for the advice, John. I built a small wheel and attached the cards and then the reader in close proximity. I can spin the wheel and the reader will read the card ID's and send keyboard codes as each segment reaches the top of the wheel. The number of reads, of course, depends on the number of rotations. It can vary quite a bit and no discernable pattern can be obtain by this.

The logic for this application would be as follows:
If the card is read for 3 seconds Keyboard.write ( ) // play the video ~ 60 seconds then stop
spin the wheel and repeat the loop.

This seems like the best way to tell when the wheel has stopped moving. Assign a timer to the reader to ensure one card ID has been read for 3 seconds and play the corresponding video.

Any thoughts?