I'm building an audio player similar to a Tonibox using an ESP32 (https://wiki.dfrobot.com/FireBeetle_Board_ESP32_E_SKU_DFR0654), a DAC, an SD card reader and a PN532 NFC reader. Due to limited pins, I had to put the SD card reader and the PN532 NFC reader on the same SPI lane. Everything initializes correctly meaning that the SD card is recognized, a start up sound is played and the NFC reader is recognized. However, the SPI setup of the SD card interferes with the NFC reader and as soon as the SD card is accessed again after the NFC was initialized, the NFC reader stops working.
I use the following libraries: ESP32-audioI2S for handling everything related to audio: https://github.com/schreibfaul1/ESP32-audioI2S SdFat for the SD card (also used by the ESP32-audioI2S: https://github.com/greiman/SdFat NDEF: https:https://github.com/don/NDEF PN532: https://github.com/Seeed-Studio/PN532
Here is a minimal working (non-working...) example:
#include <Arduino.h>
#include <PN532_SPI.h>
#include <PN532.h>
#include <SdFat.h>
#include <NfcAdapter.h>
#define SPI_MOSI 23
#define SPI_MISO 19
#define SPI_SCK 18
#define NFC_CS 2
#define SD_CS 15
#define SD_CONFIG SdSpiConfig(SD_CS, SHARED_SPI, SD_SCK_MHZ(25))
PN532_SPI pn532spi(SPI, NFC_CS);
NfcAdapter nfc = NfcAdapter(pn532spi);
SdFat SD;
void setup(void)
{
Serial.begin(115200);
// initialize SPI
pinMode(SPI_SCK, OUTPUT);
pinMode(SPI_MOSI, OUTPUT);
pinMode(SPI_MISO, INPUT);
SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
SPI.setFrequency(1000000); // Todo: check if this works
// initialize SD
pinMode(SD_CS, OUTPUT);
digitalWrite(SD_CS, HIGH);
if (SD.begin(SD_CONFIG))
{
Serial.println("SD initialized.");
SD.ls(&Serial, LS_R);
}
// initialize NFC
pinMode(NFC_CS, OUTPUT);
// digitalWrite(SD_CS, LOW);
digitalWrite(NFC_CS, HIGH);
nfc.begin();
if (nfc.tagPresent())
{
nfc.format();
nfc.erase();
NdefMessage message = NdefMessage();
message.addTextRecord("card1");
nfc.write(message);
Serial.println("Tag formatted!");
}
}
void loop(void)
{
if (nfc.tagPresent())
{
NfcTag tag = nfc.read();
Serial.println(tag.getTagType());
Serial.print("UID: ");Serial.println(tag.getUidString());
SD.ls(&Serial, LS_R);
nfc.begin(); // attempt to reinitialize SPI to make it work with the NFC reader
// everything after this line is copied from the examples of the NDEF library
if (tag.hasNdefMessage()) // every tag won't have a message
{
NdefMessage message = tag.getNdefMessage();
Serial.print("\nThis NFC Tag contains an NDEF Message with ");
Serial.print(message.getRecordCount());
Serial.print(" NDEF Record");
if (message.getRecordCount() != 1) {
Serial.print("s");
}
Serial.println(".");
// cycle through the records, printing some info from each
int recordCount = message.getRecordCount();
for (int i = 0; i < recordCount; i++)
{
Serial.print("\nNDEF Record ");Serial.println(i+1);
NdefRecord record = message.getRecord(i);
// NdefRecord record = message[i]; // alternate syntax
Serial.print(" TNF: ");Serial.println(record.getTnf());
Serial.print(" Type: ");Serial.println(record.getType()); // will be "" for TNF_EMPTY
// The TNF and Type should be used to determine how your application processes the payload
// There's no generic processing for the payload, it's returned as a byte[]
int payloadLength = record.getPayloadLength();
byte payload[payloadLength];
record.getPayload(payload);
// Print the Hex and Printable Characters
Serial.print(" Payload (HEX): ");
PrintHexChar(payload, payloadLength);
// Force the data into a String (might work depending on the content)
// Real code should use smarter processing
String payloadAsString = "";
for (int c = 0; c < payloadLength; c++) {
payloadAsString += (char)payload[c];
}
Serial.print(" Payload (as String): ");
Serial.println(payloadAsString);
// id is probably blank and will return ""
String uid = record.getId();
if (uid != "") {
Serial.print(" ID: ");Serial.println(uid);
}
}
}
}
else{
Serial.println("Looking for a tag");
}
delay(1000);
}
I did expect that switching from one SPI device to the other should be achieved by switching the respective CS pin to low while switching the other one to high. However, this didn't work for me.
As shown in the example above, I tried to reinitialize the NFC reader. This worked at least once but at some point it will always fail. The PN532 library mentions that it only works with SPI mode 0 but I don't know what that means and did not find the exact same definition in the ESP32 documentation.
Finally, I tried to initialize the SD card on vspi as suggested here by
hcheung: Using RFID and SD card Reader at the same time with SPI and ESP32 not working
This works but very inconsistently.
SPIClass *vspi = new SPIClass(VSPI);
#define SD_CONFIG SdSpiConfig(SD_CS, SHARED_SPI, SD_SCK_MHZ(25), vspi)
How could I circumevent this problem? I suspect I could initialize the SdFat/Audio library with the same mode as PN532 but I am not sure how.