ST25DV64KC - how to write a simple text string to payload

(originally posted on Arduino forum with no useful responses so posting it here if anyone has insight)

I’m a newbie to NFC and am using a Sparkfun ST25DV64KC. My microcontroller is a ESP32 (Firebeetle). I’m using the ST25DV64KC Arduino Library provided by sparkfun and specifically I’m starting with “Example 10-NDEF_Text” Listed below. On my apple phone I’m using the NFC_tools app. Everything works correctly when I upload and then use NFC_tools to read the text record/s.

What I’m wanting to do, and cannot figure it out, is instead of in the payload of the record display Hex values, instead I would like to display a string of text (perhaps using an char array) followed by the current value of a variable in my program, something like the following:

Days user has taken their medication = 3

where “Days user has taken their medication” is the string and 3 is the variable. I do not need to store the string or the variable in eeprom (or maybe I do?). Using the Example 10-NDEF_Text as a basis, can someone give me guidance on how to do this?

Here is the Example 10

/*
  ST25DV64KC Example
  By: Ricardo Ramos and Paul Clark
  SparkFun Electronics
  Date: August, 2022
  License: MIT. Please see the license file for more information but you can
  basically do whatever you want with this code.

  This example shows how to set up the ST25DV64KC's Capability Container (CC)
  and create a NDEF UTF-8 Text record

  Feel like supporting open source hardware?
  Buy a board from SparkFun!
  SparkFun Qwiic RFID Tag - ST25DV64KC : https://www.sparkfun.com/products/19035

  Hardware Connections:
  Plug a Qwiic cable into the Qwiic RFID Tag and a RedBoard
  If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
  Open the serial monitor at 115200 baud to see the output
*/

#include <SparkFun_ST25DV64KC_Arduino_Library.h> // Click here to get the library:  http://librarymanager/All#SparkFun_ST25DV64KC

SFE_ST25DV64KC_NDEF tag;

void setup()
{
  delay(1000);

  Serial.begin(115200);
  Wire.begin();

  Serial.println(F("ST25DV64KC example."));

  if (!tag.begin(Wire))
  {
    Serial.println(F("ST25 not detected. Freezing..."));
    while (1) // Do nothing more
      ;
  }

  Serial.println(F("ST25 connected."));

  // -=-=-=-=-=-=-=-=-

  // The previous examples will have left the memory write-enabled.
  // We should not need to open a security session here...
  /*
  Serial.println(F("Opening I2C security session with default password (all zeros)."));
  uint8_t password[8] = {0x0}; // Default password is all zeros
  tag.openI2CSession(password);

  Serial.print(F("I2C session is "));
  Serial.println(tag.isI2CSessionOpen() ? "opened." : "closed.");
  */

  // -=-=-=-=-=-=-=-=-

  // Clear the first 256 bytes of user memory
  uint8_t tagMemory[256];
  memset(tagMemory, 0, 256);

  Serial.println("Writing 0x0 to the first 256 bytes of user memory.");
  tag.writeEEPROM(0x0, tagMemory, 256);

  // -=-=-=-=-=-=-=-=-

  // Write the Type 5 CC File - starting at address zero
  Serial.println(F("Writing CC_File"));
  tag.writeCCFile8Byte();

  // -=-=-=-=-=-=-=-=-

  // Write two NDEF UTF-8 Text records
  uint16_t memLoc = tag.getCCFileLen();

  Serial.println(F("Writing the first NDEF Text record"));
  tag.writeNDEFText("Hello, World!", &memLoc, true, false); // MB=1, ME=0
  
  Serial.println(F("Writing the second NDEF Text record"));
  tag.writeNDEFText("All your base are belong to us", &memLoc, false, true); // MB=0, ME=1

  // -=-=-=-=-=-=-=-=-

  // Read back the second NDEF UTF-8 Text record
  Serial.println(F("Reading the second NDEF Text record:"));
  char theText[40];
  if (tag.readNDEFText(theText, 40, 2))
    Serial.println(theText);
  else
    Serial.println(F("Read failed!"));

  // -=-=-=-=-=-=-=-=-

  // Read back the memory contents
  Serial.println(F("The first 256 bytes of user memory are:"));
  tag.readEEPROM(0x0, tagMemory, 256);
  prettyPrintChars(tagMemory, 256);
}

void loop()
{
  // Nothing to do here
}

void prettyPrintChars(uint8_t *theData, int theLength) // Pretty-print char data in HEX and ASCII format
{
  Serial.println();

  for (int i = 0; i < theLength; i += 16)
  {
    if (i < 10000)
      Serial.print(F("0"));
    if (i < 1000)
      Serial.print(F("0"));
    if (i < 100)
      Serial.print(F("0"));
    if (i < 10)
      Serial.print(F("0"));
    Serial.print(i);

    Serial.print(F(" 0x"));

    if (i < 0x1000)
      Serial.print(F("0"));
    if (i < 0x100)
      Serial.print(F("0"));
    if (i < 0x10)
      Serial.print(F("0"));
    Serial.print(i, HEX);

    Serial.print(F(" "));

    int j;
    for (j = 0; ((i + j) < theLength) && (j < 16); j++)
    {
      if (theData[i + j] < 0x10)
        Serial.print(F("0"));
      Serial.print(theData[i + j], HEX);
      Serial.print(F(" "));
    }

    if (((i + j) == theLength) && (j < 16))
    {
      for (int k = 0; k < (16 - (theLength % 16)); k++)
      {
        Serial.print(F("   "));
      }
    }

    for (j = 0; ((i + j) < theLength) && (j < 16); j++)
    {
      if ((theData[i + j] >= 0x20) && (theData[i + j] <= 0x7E))
        Serial.write(theData[i + j]);
      else
        Serial.print(F("."));
    }

    Serial.println();
  }

  Serial.println();
}

The other alternative that would satisfy my requirements is to write an sprintf to the tag.writeNDEFText():

 Serial.println(F("Writing the first NDEF Text record"));
  Serial.println("Days user has taken their medication = %d");
  char buffer[40];
  int days = 3;
  sprintf(buffer, "Days user has taken their medication = %d", days);
  tag.writeNDEFText(buffer, &memLoc, false, true);  // MB=0, ME=1

Unfortunately this does not seem to work - when uploaded and run serial monitor (or scaning on my mobile NFC reader app on mobile)

Writing 0x0 to the first 256 bytes of user memory.
Writing CC_File
Writing the first NDEF Text record
Days user has taken their medication = %d
Writing the second NDEF Text record
Reading the second NDEF Text record:
Read failed!
The first 256 bytes of user memory are:

00000 0x0000 E2 40 00 01 00 00 03 FF 51 55 2B 54 02 65 6E 44 .@…QU+T.enD
00016 0x0010 61 79 73 20 75 73 65 72 20 68 61 73 20 74 61 6B ays user has tak
00032 0x0020 65 6E 20 74 68 65 69 72 20 6D 65 64 69 63 61 74 en their medicat
00048 0x0030 69 6F 6E 20 3D 20 33 51 01 21 54 02 65 6E 41 6C ion = 3Q.!T.enAl
00064 0x0040 6C 20 79 6F 75 72 20 62 61 73 65 20 61 72 65 20 l your base are
00080 0x0050 62 65 6C 6F 6E 67 20 74 6F 20 75 73 FE 00 00 00 belong to us…
00096 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00112 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00128 0x0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00144 0x0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00160 0x00A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00176 0x00B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00192 0x00C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00208 0x00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00224 0x00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …
00240 0x00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …

Perhaps the tag.writeNDEF function does not accept replacing the quotation marks with buffer?

Hi Mark (@mbiasotti ),

sprintf should work well for your application.

Looking at the memory dump in your second post, the code has written “Days user has taken their medication = 3” to the first text record. But in your code you used the parameters: false, true); // MB=0, ME=1 . That’s only valid if you are writing two or more records and this is the final record. Message Begin not true; Message End true.

If you write only one record, use: true, true); // MB=1, ME=1

If this is the first of (e.g.) two records, use: true, false); // MB=1, ME=0
The final record should have ME set true.

The code documentation is here.

Also “Days user has taken their medication = 3” is exactly 40 characters. Your buffer needs to be large enough to hold the full message plus a NULL (0) terminating character. I’d suggest increasing the size of buffer to 50 to give yourself some headroom. The writeNDEFText will stop writing when it finds the NULL. snprintf is memory-safe and can prevent you overunning the buffer.

I hope this helps,
Paul

@PaulZC Thank you. This is what I was missing and now understand how the booleans work in tag.writeNDEFText().

This is now how I’ve written it and it displays correctly with my NFC Tools mobile app when displaying the record:

Serial.println(F("Writing the first NDEF Text record"));
  char buffer[80];
  int days = 3;
  int alarm1_hr = 8;
  int alarm1_min = 30;
  sprintf(buffer, "READ USER 1 has taken their medication for %d days. Alarm Time is set for %d:%d ", days, alarm1_hr, alarm1_min);
  tag.writeNDEFText(buffer, &memLoc, true, true);  // MB=1, ME=1

No problem Mark - glad that’s working for you,
Paul

PS. your buffer[80] is only just large enough. You will overflow the buffer if days, alarm1_hr and alarm1_min are 2-digit. I’d suggest increasing the size of buffer…

@PaulZC okay I will - its seems to work at 80.