NFC
Device Compatibility
Overview
The NFC abstraction supports:
- Reading Info (All tags)
- Reading Page (MIFARE Ultralight & NTAG)
- Writing Page (MIFARE Ultralight & NTAG)
- Reading NDEF (MIFARE Ultralight & NTAG)
- Writing NDEF (MIFARE Ultralight & NTAG)
References
Below is the overview of the NFC abstraction. Code examples can be found here.
These header files are required to use NFC.
// The main library import #include "matrix_nfc/nfc.h" // Holds data for NFC #include "matrix_nfc/nfc_data.h"
Only one instance of NFC is supported at a time, making multiple instances of the NFC object may lead to undesired behavior.
Most functions that execute NFC sensor commands return status codes, and the NFCStatus
function can be used to get a string corresponding to the status code, the examples below show usage.
Classes for NFC tag commands
NFC
The main NFC
class has an instance of the NDEF
class, and an instance of the MFUL
& NTAG
classes. These classes are used to perform NDEF operations and page read/write operations on NFC tags.
// Create NFC object matrix_hal::NFC nfc;
.Activate
The function Activate
activates an NFC tag. This is required before using any other function.
// Usage int status_code = nfc.Activate(); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Function declaration in header file int Activate();
.Deactivate
The function Deactivate
deactivates an NFC tag. This stops communications between the NFC sensor and NFC tag and should be performed after the desired command has finished.
// Usage int status_code = nfc.Deactivate(); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Function declaration in header file int Deactivate();
.ReadInfo
The function ReadInfo
accepts an InfoContent
object and populates it with tag information.
// Usage matrix_hal::NFCData data = NFCData(); // Must first activate card nfc.Activate(); int status_code = nfc.ReadInfo(&data.info); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int ReadInfo(InfoContent* nfc_info);
NDEF
The NDEF
class is used for NDEF operations on tags. This class is nested within the NFC
class above, and should not be manually instantiated.
// Create NFC object matrix_hal::NFC nfc;
.Read
The function Read
reads the NDEF content from an NFC tag and populates a NDEFContent
object with the read NDEF information. For more info see the NDEFContent
class.
// Usage matrix_hal::NFCData data = NFCData(); // Must first activate card nfc.Activate(); int status_code = nfc.ndef.Read(&data.ndef); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int Read(NDEFContent* ndef_content);
.Write
The function Write
writes provided NDEF content to a NFC tag. It is an overloaded function, accepting either a pointer to a NDEFContent
object or a pointer to a NDEFParser
object. For more info see the NDEFContent
and NDEFParser
classes.
// Usage matrix_hal::NFCData data = NFCData(); // Must first activate card nfc.Activate(); int status_code = nfc.ndef.Write(&data.ndef) // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int Write(NDEFContent* ndef_content);
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Must first activate card nfc.Activate(); int status_code = nfc.ndef.Write(&ndef_parser) // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int Write(NDEFParser* ndef_parser);
.Erase
The function Erase
erases the NDEF content on a NFC tag.
// Usage // Must first activate card nfc.Activate(); int status_code = nfc.ndef.Erase(); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int Erase();
MFUL & NTAG
The MFUL
and NTAG
classes are used for page read and write operations on MIFARE Ultralight(MFUL) and NTAG type NFC tags respectively.
This class is nested within the NFC
class above, and should not be manually instantiated.
For all usage examples nfc.mful
will be used, but substituting that out for nfc.ntag
will use the functions from the NTAG class instead. Both classes have the same function names and declarations.
// Create NFC object matrix_hal::NFC nfc;
.ReadPage
The function ReadPage
accepts a page number and reads the specified page from the tag. The page is returned as a vector. If the specified page does not exist or the read fails then an empty vector will be returned.
// Usage // Must first activate card nfc.Activate(); // Read page 10 from the card int page_number = 10; std::vector<uint8_t> read_page = nfc.mful.ReadPage(page_number); // To print the vector's contents as a hex string std::cout << matrix_hal::PagesContent::BytesToString(read_page) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file std::vector<uint8_t> ReadPage(uint8_t page_number);
.ReadPages
The function ReadPages
accepts a pointer to a PagesContent
object and populates it with all read pages. For more info see the PagesContent
class.
// Usage matrix_hal::NFCData data = NFCData(); // Must first activate card nfc.Activate(); int status_code = nfc.mful.ReadPages(&data.pages); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // To print the contents of pages as hex std::cout << "Hex:\n" << data.pages.ToHex() << std::endl; // To print the contents of pages as a char string std::cout << "String:\n" << data.pages.ToString() << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int ReadPages(PagesContent* pages_content);
.WritePage
⚠️Caution: Writing to specific pages may lock or password protect tag, please view specific NFC tag IC datasheet for more information⚠️.
NFC tag IC can be found by using the ReadInfo
function (in NFC
class) and then accessing the IC_type
field in InfoContent
object.
The function WritePage
accepts a page number and a byte vector and writes the byte vector to the desired page.
For MFUL and NTAG type cards the page length is always 4.
Passing a vector with a length above 4 will result in truncation.
Passing a vector with a length below 4 will result in undefined behavior.
// Usage // Must first activate card nfc.Activate(); // Write "taco" to page 10 int page_number = 10; std::string write_str = "taco"; std::vector<uint8_t> write_data = std::vector<uint8_t>(write_str.begin(), write_str.end()); int status_code = nfc.mful.WritePage(page_number, write_data); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Function declaration in header file int WritePage(uint8_t page_number, std::vector<uint8_t>& write_data);
Classes for NFC data storage
NFCData
The NFCData
class has an instance of the InfoContent
class, PagesContent
class and NDEFContent
class.
// Create NFCData object matrix_hal::NFCData data = NFCData();
// All class data members InfoContent info = InfoContent(); PagesContent pages = PagesContent(); NDEFContent ndef = NDEFContent();
InfoContent
The InfoContent
class holds tag identification information.
// Create NFCData object matrix_hal::NFCData data = NFCData();
// All class data members bool recently_updated = false; // Updated to true upon successful read, else set to false std::vector<uint8_t> UID; // Unique tag id std::vector<uint8_t> ATQ; int SAK = -1; int bit_rate = -1; // In bits/second (baud) std::string technology = "null"; std::string type = "null"; std::string card_family = "null"; std::string IC_type = "null"; int storage_size = -1; // In bytes
All examples below assume that an NFCData object was created and that the info
member has already been populated by use of ReadInfo
(from NFC
class).
.UIDToHex
The function UIDToHex
will return the UID as a hex string.
// Usage std::string UID = data.info.UIDToHex(); // Function declaration in header file std::string UIDToHex();
.ATQToHex
The function ATQToHex
will return the ATQ as a hex string.
// Usage std::string ATQ = data.info.ATQToHex(); // Function declaration in header file std::string ATQToHex();
.SAKToHex
The function SAKToHex
will return the SAK as a hex string.
// Usage std::string SAK = data.info.SAKToHex(); // Function declaration in header file std::string SAKToHex();
.ToString
The function ToString
will return a string with all class parameters.
// Usage std::string info_string = data.info.ToString(); // Function declaration in header file std::string ToString();
NDEFContent
The NDEFContent
class holds the NDEF data that was read.
// Create NFCData object matrix_hal::NFCData data = NFCData();
// All class data members bool recently_updated = false; // Updated to true upon successful read, to false if read did not succeed or if card was not activated bool valid = false; // Updated to true if NDEF is valid, updated to false if NDEF is not detected std::vector<uint8_t> content; // Raw NDEF message read from the NFC Tag
All examples below assume that an NFCData object was created and that the ndef
member has already been populated by use of Read
(from NDEF
class).
.ToHex
The function ToHex
will return the NDEF data as a hex string.
// Usage std::string hex_ndef = data.ndef.ToHex(); // Function declaration in header file std::string ToHex();
.ToString
The function ToString
will return the NDEF data as a string, treating each byte as a character.
If the character is not printable it will be replaced with '.'.
If NDEF is not detected then "NDEF is Not Detected" will be returned.
If NDEF is null then "NDEF is NULL" will be returned.
// Usage std::string string_ndef = data.ndef.ToString(); // Function declaration in header file std::string ToString();
PagesContent
The PagesContent
class holds the pages that were read.
// Create NFCData object matrix_hal::NFCData data = NFCData();
// All class data members bool recently_updated = false; // True if new tag was read, false if no tag found bool read_complete = false; // True if all pages were read, false if read was interrupted std::vector<std::vector<uint8_t>> content; // All data read from the NFC Tag, by page
All examples below assume that an NFCData object was created and that the pages
member has already been populated by use of the ReadPages
function (from MFUL
and NTAG
classes).
.ToHex
The function ToHex
will return all pages as a string. Every page will be on a new line and will be prefixed with "Page HEX: " where HEX is a hex number. Each byte will be outputted as hex.
// Usage std::string hex_pages = data.pages.ToHex(); // Function declaration in header file std::string ToHex();
.ToString
The function ToString
will return all pages as a string, treating each byte as a character.
If the character is not printable it will be replaced with '.'.
// Usage std::string string_pages = data.pages.ToString(); // Function declaration in header file std::string ToString();
The following functions are static and can be used to convert a byte (uint8_t) vector to a string.
.BytesToHex
The function BytesToHex
will convert a uint8_t vector to a hex string and return the string. Each byte will be outputted as hex.
// Usage std::vector<uint8_t> page = nfc.mful.ReadPage(42); std::string page_hex_string = matrix_hal::PagesContent::BytesToHex(read_page); // Function declaration in header file static std::string BytesToHex(const std::vector<uint8_t> &vec);
.BytesToString
The function BytesToString
will convert a uint8_t vector to a string and return the string. Each byte will be treated as a character.
If the character is not printable it will be replaced with '.'.
// Usage std::vector<uint8_t> page = nfc.mful.ReadPage(42); std::string page_char_string = matrix_hal::PagesContent::BytesToString(read_page); // Function declaration in header file static std::string BytesToString(const std::vector<uint8_t> &vec);
Classes for NDEF Parsing
NDEFParser
The NDEFParser
class is used to parse and modify NDEF messages. An instance of this object can be written to an NFC tag with the Write
function (from NDEF
class).
This class has multiple constructors.
// Create an empty NDEFParser object matrix_hal::NDEFParser ndef_parser = NDEFParser();
// Code to populate data.ndef with an NDEF message read from tag matrix_hal::NFCData data = NFCData(); // Must first activate card nfc.Activate(); int status_code = nfc.ndef.Read(&data.ndef); // To print a string corresponding to the status code std::cout << matrix_hal::NFCStatus(status_code) << std::endl; // Deactivate the card after completing desired action nfc.Deactivate(); // Create a NDEFParser object using the data from a NDEFContent object matrix_hal::NDEFParser ndef_parser = NDEFParser(&data.ndef);
.AddUriRecord
The function AddUriRecord
will add a uri record to the NDEFParser
. When a tag containing this NDEF record is scanned by an android phone it will open the uri in a browser.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add uri record ndef_parser.AddUriRecord("http://docs.matrix.one"); // Function declaration in header file void AddUriRecord(std::string uri);
.AddTextRecord
The function AddTextRecord
will add a text record to the NDEFParser
. This function is overloaded, optionally accepting an encoding. If no encoding if provided then "en" is used.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add text record ndef_parser.AddTextRecord("hello world!"); // Function declaration in header file void AddTextRecord(std::string text);
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add text record ndef_parser.AddTextRecord("hola mundo!", "es"); // Function declaration in header file void AddTextRecord(std::string text, std::string encoding);
.AddEmptyRecord
The function AddEmptyRecord
will add a empty NDEF record to the NDEFParser
.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add empty record ndef_parser.AddEmptyRecord(); // Function declaration in header file void AddEmptyRecord();
.AddMimeMediaRecord
The function AddMimeMediaRecord
will add a MIME media NDEF record to the NDEFParser
. For more information on MIME media types see Mozilla's MIME type documentation. This function is overloaded and can either accept a uint8_t payload or a string payload.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add MIME media record ndef_parser.AddMimeMediaRecord("text/plain", "hello world!"); // Function declaration in header file void AddMimeMediaRecord(std::string mimeType, std::string payload);
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Add MIME media record std::vector<uint8_t> payload = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'}; ndef_parser.AddMimeMediaRecord("text/plain", payload.data(), payload.size()); // Function declaration in header file void AddMimeMediaRecord(std::string mimeType, uint8_t* payload, int payloadLength);
.AddRecord
The function AddRecord
will add a NDEFRecord
to the NDEFParser
. This can be used to add custom NDEF records to a NDEF message. For more info see the NDEFRecord
class.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); matrix_hal::NDEFRecord ndef_record = NDEFRecord(); // Modify ndef_record as needed // Add record to ndef_parser ndef_parser.AddRecord(ndef_record); // Function declaration in header file bool AddRecord(NDEFRecord& record);
.ToString
The function ToString
will return a string with information about the NDEF message.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); std::string ndef_parser_string = ndef_parser.ToString(); // Function declaration in header file std::string ToString();
.GetEncodedSize" & ".Encode
The function GetEncodedSize
will return the size of the encoded representation of the NDEF message. It is used to create a uint8_t
array large enough to store the encoded message.
The function Encode
will encode the NDEF message into the proper format for writing to an NFC tag.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); int size = ndef_parser.GetEncodedSize(); uint8_t encoded_ndef_message[size]; // Populate encoded_ndef_message ndef_parser.Encode(encoded_ndef_message); // Function declarations in header file int GetEncodedSize(); void Encode(uint8_t* data);
.GetRecordCount" & ".GetRecord
The function GetRecordCount
will return the number of NDEF records inside the NDEF message.
The function GetRecord
will return the NDEF record at a specified index.
The NDEFParser class also overloads the bracket operator to return the NDEF record at a specified index.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); int size = ndef_parser.GetRecordCount(); // Get the last NDEF record matrix_hal::NDEFRecord last_record = ndef_parser.GetRecord(size - 1); // Alternatively Using operator[] overload instead of .GetRecord function matrix_hal::NDEFRecord last_record = ndef_parser[size - 1]; // Function/operator declarations in header file unsigned int GetRecordCount(); NDEFRecord GetRecord(int index); NDEFRecord operator[](int index);
NDEFRecord
The NDEFRecord
class is used to create custom NDEF records, for use with NDEFParser
.
// Create an empty NDEFRecord object matrix_hal::NDEFRecord ndef_record = NDEFRecord();
.GetPayloadLength" & ".GetPayload
The function GetPayloadLength
will return the length of the NDEF record's payload parameter.
The function GetPayload
will populate a uint8_t array with the NDEF record's payload parameter.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Get first record's payload int payload_size = first_record.GetPayloadLength(); uint8_t* payload = new uint8_t[payload_size]; // Function declarations in header file int GetPayloadLength(); void GetPayload(uint8_t *payload);
.GetTypeLength" & ".GetType
The function GetTypeLength
will return the length of the NDEF record's type parameter
The function GetType
will populate a uint8_t array with the NDEF record's type parameter
The GetType
function is also overloaded, returning a string of the type parameter instead if called with no argument.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Get first record's type int type_size = first_record.GetTypeLength(); uint8_t* type = new uint8_t[type_size]; // Alternatively get type string std::string type_string = first_record.GetType(); // Function declarations in header file unsigned int GetTypeLength(); void GetType(uint8_t *type); std::string GetType();
.GetIdLength" & ".GetId
The function GetIdLength
will return the length of the NDEF record's ID parameter.
The function GetId
will populate a uint8_t array with the NDEF record's ID parameter.
The GetId
function is also overloaded, returning a string of the ID parameter instead if called with no argument.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Get first record's ID int id_size = first_record.GetTypeLength(); uint8_t* type = new uint8_t[id_size]; // Alternatively get ID string std::string id_string = first_record.GetId(); // Function declarations in header file unsigned int GetIdLength(); void GetId(uint8_t *id); std::string GetId();
.GetTnf
The function GetTnf
will return a uint8_t of the NDEF record's TNF parameter.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Get first record's TNF uint8_t tnf = first_record.GetTnf(); // Function declaration in header file uint8_t GetTnf();
.SetTnf
The function SetTnf
will set the NDEF record's TNF parameter to the value passed in.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Set first record's TNF uint8_t tnf_to_set = 0; first_record.SetTnf(tnf_to_set); // Function declaration in header file void SetTnf(uint8_t tnf);
.SetType
The function SetType
will set the NDEF record's type parameter to the value passed in.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Set first record's type int size = 20; uint8_t type_to_set[size] = {}; first_record.SetType(type_to_set, size); // Function declaration in header file void SetType(const uint8_t *type, const unsigned int numBytes);
.SetPayload
The function SetPayload
will set the NDEF record's payload parameter to the value passed in.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Set first record's payload int size = 20; uint8_t payload_to_set[size] = {}; first_record.SetType(payload_to_set, size); // Function declaration in header file void SetPayload(const uint8_t *payload, const int numBytes);
.SetId
The function SetId
will set the NDEF record's ID parameter to the value passed in.
// Usage matrix_hal::NDEFParser ndef_parser = NDEFParser(); // Adding some records to NDEFParser ndef_parser.AddUriRecord("http://docs.matrix.one"); ndef_parser.AddTextRecord("hello world!"); // Get the first record matrix_hal::NDEFRecord first_record = ndef_parser[0]; // Set first record's ID int size = 20; uint8_t id_to_set[size] = {}; first_record.SetId(id_to_set, size); // Function declaration in header file void SetId(const uint8_t *id, const unsigned int numBytes);