Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development Knowledge Base

NFC on BlackBerry 10 - Reading and Writing Tags using native APIs

by Retired on ‎06-08-2012 10:22 AM - edited on ‎11-02-2012 09:10 AM by Retired (13,747 Views)

Introduction

 

This article is part of a series intended to help developers wishing to exploit NFC in their BlackBerry® 10 applications. Readers of this article should have some pre-existing knowledge of the fundamental architecture of NFC systems and be familiar with C++. It would be beneficial to have read at least the first parts of the corresponding article from the BlackBerry® 7 series of articles entitled: "Reading and Writing NFC Tags" since this covers tag concepts which are as applicable to BlackBerry 10 as they are to BlackBerry 7. The BlackBerry 7 article can be found here.

 

This article will explain how to use the BlackBerry 10 C++ native API to develop software that can read and write NFC tags.

 

This is a revised version of the article that addresses the updated BlackBerry Dev Alpha device running version 10.0.09 (AKA beta 3) of the BlackBerry 10 platform.

 

The Authors

 

This article was co-authored by Martin Woolley and John Murray both of whom work in the RIM Developer Relations team. Both Martin and John specialize in NFC applications development (amongst other things).

 

About NFC Tags

 

NFC tags are essentially contactless memory cards that store a message in a standard format. When a tag comes into proximity with an NFC reader, the content of the tag is transferred across the contactless interface to the reader from where it is usually dispatched to an appropriate application for processing. The standard format used for messages stored on NFC tags is called the NFC Data Exchange Format or “NDEF” for short.

 

The NFC Forum defines four types of tag, known as Type 1, Type 2, Type 3 and Type 4. The differences between the types include their maximum storage capacity and supported protocols. Types 1-3 only allow communication at layer 3 of the ISO14443 NFC protocol stack whereas a Type 4 tag supports layer 4 and ISO7816-4 APDUs can be used to communicate with such a tag.

 

Physically, tags come in a variety of shapes and sizes. Figure 1 shows three tags, one type 1 tag in the form of a wrist band, one type 1 tag in the form of a key fob and the third, a type 2 tag in the form of a card. A useful format is for the tag to be embedded in a self-adhesive paper sticker so that it can be easily attached to surfaces.

 

tags.png

Figure 1 - Tags

 

NDEF messages stored on a tag contain one or more NDEF records. Various types of record are defined including a selection of commonly used types defined by the NFC Forum as “well known types”. Well known types include Text, URI and Smart Poster.

 

On BlackBerry 10, developers can inform the system that their application is interested in NFC tags containing records of one or more types and to receive and process tag related data when encountered by the BlackBerry device. Additionally, the native APIs allow applications to detect the proximity of a tag and write an NDEF message to it. Both these use cases are described in this article in the following sections.

 

NFC Tool - The Sample Application

 

IMG_00000042.png

Figure 2 - NFC Tool's home screen

 

To accompany this article, we’ve written an application called “NFC Tool” which demonstrates how to read and write tags. The application was written in C++ using the BlackBerry 10 NDK. We’ve used the Cascades™ framework for the user interface along with the powerful Qt framework for certain other aspects. "NFC Tool" contains functionality in addition to tag reading and writing, which are the subject of other articles in this series.

 

In the sections that follow, we’ll use code fragments from NFC Tool to illustrate exactly how to proceed in your own code. We’ve released the application code in full in our GitHub® repository, details of which can be found at the end of this article.

 

NFC Tool - Design and Implementation

 

Before we get into the NFC aspects of NFC Tool, let’s review the basic design and implementation approach taken for this application. Figure 3 gives an overview of the basic flow between pages. Note that not all pages are shown.

 

UI overview.png

Figure 3 - NFC Tool user interface overview

 

For the purposes of this article, we’ll look at the following functions those NFC Tool offers, each of which is selectable from the menu page:

  1. Read – Read an NFC tag
  2. Write URI – Write a tag with well-known type “U”
  3. Write Smart Poster – Write a tag with well-known type “Sp”
  4. Write Text – Write a tag with well-known type “T”
  5. Write Custom – Write a tag with “external” TNF and a custom type value
  6. About – Show the about page with information about NFC Tool

Common to functions 1-5 is an event log page that displays information about tag detection, reading or writing activity as it takes place. The purpose of this page is to provide visibility of the key events occurring during execution of your selected function so as to aid understanding of the process.

 

The basic UI design is simple. The home screen provides a list of menu items that you can select by touching. We chose a list oriented approach because we intend to add further menu items in the future and a scrollable list is easily extended in this way.

 

The implementation follows a fairly simple structure too. We used Cascades QML for all presentation layer aspects of the application. Control logic was implemented in C++ using Qt Signals and Slots. For NFC operations, we call BlackBerry 10 platform APIs and wrap the NFC operations in a class called NfcManager which acts like a facade for other classes to use. We also have a class called NfcWorker that allows us to execute NFC operations in their own thread. This is important because reading NFC event objects using the BlackBerry Platform Services library functions involves making a blocking call and we do not want this to interfere with our user experience.

 

Selecting an item from the menu causes a new page to slide in from the right. This behavior is a consequence of the Cascades component we used for the main menu page and I’ll come to this shortly. The great thing about Cascades is that “implicit animations”, such as this sliding behavior, are completely free of charge. We had to do no explicit coding to create this attractive UI effect.

 

Since the user interface was created using Cascades, we have QML documents for each page. The main page, main.qml uses the NavigationPane component. The NavigationPane component provides your UI with a stack oriented or drill down structure. Navigating to the next page involves pushing a page object onto a stack. The item at the top of the stack is displayed to the user, usually with an automatic, implicit animation, as described.

 

All pages apart from the main menu feature one or more buttons at the foot of each page. This too is a feature of NavigationPane. The back button is provided by default and requires no coding unless you want to do something non-standard. By default, the back button pops the current page from the top of the stack so that the user “goes back to” the previous page. You can add your own buttons too of course.

 

Figure 4 shows part of the main.qml page. Note that we load details of the items that the menu list contains from an XML file. The contents of this file are shown in Figure 5.

 

NavigationPane {
    id: nav
    objectName: "navPane"
        
    Page {
        id: menuListPage
        
        content: Container {
            background: Color.create ("#262626")
            preferredWidth: 768
            layout: DockLayout {
            }

            Container {
                layout: DockLayout {
                    topPadding: 2
                    bottomPadding: 2
                }
                ListView {
                    id: menuList;
                    objectName: "list"
                    dataModel: XmlDataModel {
                        source: "models/menumodel.xml"
                    }
                                        
                    listItemComponents: [
                        ListItemComponent {
                            type: "menuitem" 
                            MenuItem {
                            }
                        }
                    ]
                    
                }
            }
        }
    }
    onTopChanged: {
        if (page == menuListPage) {
            // Clear selection when returning to the menu list page.
            menuList.clearSelection ();
        }
    }
}

Figure 4 - QML for the main menu page

 

<root>
    <menuitem title="Read"                   image="asset:///images/read.png" file="reader.qml" itemName="item_read"/>
    <menuitem title="Write URI"              image="asset:///images/uri.png" file="write_uri.qml" itemName="item_uri"/>
    <menuitem title="Write Smart Poster"     image="asset:///images/sp.png" file="write_sp.qml" itemName="item_sp"/>
    <menuitem title="Write Text"             image="asset:///images/text.png" file="write_text.qml" itemName="item_text"/>
    <menuitem title="Write Custom"           image="asset:///images/custom.png" file="write_custom.qml" itemName="item_custom"/>
    <menuitem title="Send vCard (SNEP)"      image="asset:///images/snep.png" file="snep_vcard.qml" itemName="item_snep_vcard"/>
    <menuitem title="Emulate Tag"            image="asset:///images/tag.png" file="emulate_sp.qml" itemName="item_emulate_tag"/>
    <menuitem title="ISO7816 APDU"           image="asset:///images/iso7816.png" file="" itemName="item_iso7816"/>
    <menuitem title="About"                  image="asset:///images/about.png" file="about.qml" itemName="item_about"/>
</root>

 

 Figure 5 - Main menu XML data

 

Data from the XML file is bound to the UI component with which it is associated and individual attributes can be referenced using the ListItemData alias and the appropriate attribute name from the XML. Figure 6 shows part of the MenuItem.qml file that specifies the markup for an individual menu item in the list.

 

 

            ImageView {
                // The image is bound to the data in models/recipemodel.xml image attribute.
                imageSource: ListItemData.image
                leftMargin: 3                
            }
            
            Label {
                // The title is bound to the data in models/recipemodel.xml title attribute.
                text: ListItemData.title
                leftMargin: 30
                textStyle {
                    base: SystemDefaults.TextStyles.TitleText
                    color: Color.Black
                }
                
                layoutProperties: StackLayoutProperties {
                    verticalAlignment: VerticalAlignment.Center
                }
                
            }

 

Figure 6 - Referencing XML attributes in QML 

 

 

We implemented the greater majority of our control logic in C++ and made good use of the excellent Qt “Signals and Slots” capability. Each QML page has a corresponding C++ object that implements required slots and takes care of one or two other issues. In Figure 7 you can see how we “wired up” some of our signals and slots in the MainMenu.cpp class. The essence of the connect statement is that it means, “if this object emits this SIGNAL, then execute the following SLOT function owned by this object”.

 

 

Figure 7 sets up the SIGNAL and SLOT connections that relate to the selection of specific menu items and the resultant calling of the related object’s show() function. Figure 8 shows how to make use of the selectionChanged signal which our ListView component in the main.qml page will emit whenever the user selects a new item.

 

 

Figure 9 shows how we emit the appropriate signal from within some conditional logic in onListSelectionChanged in MainMenu.cpp. The signals emitted here correspond to the signal/slot connections shown in Figure 7. Between them, Figures 7, 8 and 9 show how we navigate to the next page from the main menu.

 

	QObject::connect(this, SIGNAL(read_selected()), _eventLog, SLOT(show()));
	QObject::connect(this, SIGNAL(write_uri()), _writeURI, SLOT(show()));
	QObject::connect(this, SIGNAL(write_sp()), _writeSp, SLOT(show()));
	QObject::connect(this, SIGNAL(write_text()), _writeText, SLOT(show()));
	QObject::connect(this, SIGNAL(write_custom()), _writeCustom, SLOT(show()));
	QObject::connect(this, SIGNAL(send_vcard_selected()), _sendVcard, SLOT(show()));
	QObject::connect(this, SIGNAL(emulate_tag_selected()), _emulateSp, SLOT(show()));
	QObject::connect(this, SIGNAL(iso7816_selected()), _apduDetails, SLOT(show()));
	QObject::connect(this, SIGNAL(about_selected()), _about, SLOT(show()));

 

 Figure 7 – Main Menu Signals and Slots

 

 

 

	QObject::connect(listView,
			SIGNAL(selectionChanged(const QVariantList, bool)), this,
			SLOT(onListSelectionChanged(const QVariantList, bool)));

 

 Figure 8 - Connecting to the ListView selectionChanged signal

 

 

 

// only part of this function is shown here
void MainMenu::onListSelectionChanged(const QVariantList indexPath,
		bool selected) {
	if (selected) {

// We use the sender to get the list view for accessing the data model and then the actual data.
		if (sender()) {
			ListView* menuList = dynamic_cast<ListView*>(sender());
			DataModel* menuModel = menuList->dataModel();
			QVariantMap map = menuModel->data(indexPath).toMap();
			if (map["itemName"].canConvert(QVariant::String)) {
				QString item = map["itemName"].toString();
				qDebug() << "selected item name=" << item;
				if (item.compare("item_read") == 0) {
					qDebug() << "Read Tag was selected!";
					startListening();
					_eventLog->setMessage("Hello");
					emit read_selected();

				} else if (item.compare("item_uri") == 0) {
					qDebug() << "Write URI was selected!";
					emit write_uri();
				} else if ..........................

  Figure 9 - Handling list selection changes

 

As mentioned, class NfcManager presents a simple, facade-like interface, which other classes can use to initiate the various NFC operations. Figure 10 shows the public functions as defined in the header file. As you may have guessed from the presence of a static function called getInstance(), NfcManager is a singleton, making it easy to locate and use from anywhere in the application.

 

public:
	static NfcManager* getInstance();
	void startEventProcessing();
	void stopNdefListener();
	void writeUri(QString* uri);
	void writeSp(QString* sp_uri, QString* sp_text);
	void writeText(QString* text);
	void writeCustom(QString* domain, QString* type, QString* payload);
	void stopNfcWorker();

   Figure 10 - NfcManager public functions

 

 

 

NfcWorker makes use of QtConcurrent::run to run code in a background thread. Java® developers should find this pattern familiar as it is not dissimilar to the Java Runnable interface. It has some nice additional features however; including the ability to associate a QFutureWatcher with the thread. QFutureWatcher monitors thread execution and through signals and slots can report progression through the thread’s various states to whatever functions you choose to connect to the signals it can emit.  Figure 11 shows how we used this in one part of NfcWorker.

 

 

void NfcManager::startEventProcessing() {
    _future = new QFuture<void>;
    _watcher = new QFutureWatcher<void>;
    _workerInstance = NfcWorker::getInstance();
    *_future = QtConcurrent::run(_workerInstance, &NfcWorker::startEventLoop);
    _watcher->setFuture(*_future);
    QObject::connect(_watcher, SIGNAL(finished()), this, SLOT(workerStopped()));
    QObject::connect(_workerInstance, SIGNAL(message(QVariant)), this,
                      SLOT(message(QVariant)), Qt::QueuedConnection);
    QObject::connect(_workerInstance, SIGNAL(clearMessages()), this,
                      SLOT(clearMessages()), Qt::QueuedConnection);
}

  Figure 11 - QtConcurrent and QFutureWatcher in NfcManager

 

 

The BlackBerry 10 Invocation Framework

 

In BlackBerry 10 OS, the Invocation Framework (IF), which was introduced in the 10.0.06 Dev Alpha release, provides the ability for the user to perform an action on an item identified as content. This framework enables the client (applications, service or viewers) to send a message to a target (applications, service or viewers) to perform a particular action. The framework also offers the capability to discover what targets are available on the device.

 

Many of the capabilities of NFC on the device have been integrated with the Invocation Framework in order to allow the developer to focus on the business logic of his application rather than having to be concerned with lower level aspects of how NFC works.

 

In particular, the reading of NFC Tags has been integrated with the Invocation Framework and in what follows we’ll show how to read NFC Tags using this framework. The diagram at the end of this section illustrates the basic relationship between the Invocation Framework and tag reading. "iF" is short hand for "Invocation Framework" by the way.

 

By integrating NFC with the Invocation Framework it means that simpler integration of NFC capabilities with the higher level APIs in Qt and QML become possible.

 

It is not the intent of this article to explore these aspects, which will be the subject of future articles.

 

if_tag_reading.png 

 

Reading NFC Tags

 

 

Introduction

 

 

BlackBerry 10 allows developers to register an interest with the Invocation Framework in particular types of NFC NDEF data. This is achieved through an entry in the bar-descriptor.xml file as will be presented below. Reading an NFC tag then involves the Invocation Framework delivering an InvokeRequest object to the application and other APIs then being used to extract the NDEF messages and records that the InvokeRequest object contains.

 

A recipe for reading NFC tags from the BlackBerry 10 NDK

 

Reading a tag is accomplished in four steps, which we present below as a kind of standard “recipe”.

 

tag_read_recipe.png

Figure 12 - Recipe for reading NFC tags

 

 

We’ll now proceed to examine each of the four steps at a code level.

 

Step 1 – Register for NDEF message types through Invocation Framework

 

This is achieved through adding a stanza in the bar-descriptor.xml file associated with you application. The information in this stanza is parsed and incorporated into your application’s BAR file as meta-data when you package and sign it. When your application is installed on the device this information is used to register your application’s interest in being notified when a number of NFC NDEF events occur.

 

    <invoke-target id="com.example.NfcTool">
        <type>APPLICATION</type>
        <filter>
            <action>bb.action.OPEN</action>
            <mime-type>application/vnd.rim.nfc.ndef</mime-type>
            <property var="uris" value="ndef://1/Sp,ndef://1/T,ndef://1/U"/>
        </filter>
	</invoke-target>

 

 Figure 13 - Registering with IF

 

Let’s look at this stanza in a bit more detail. The “<invoke-target ..>” tag identifies our application as being the target of one or more invocation framework events.

  

The next point to notice is that there is a “<filter>” element. This defines the type of invocation framework event and MIME type in which we’re interested as well as some additional URI based filters. 

  

The MIME type “application/vnd.rim.nfc.ndef” is a RIM custom MIME type that identifies as type of NFC NDEF tag. The <property> element goes on to specify URI filters and this is how we indicate which particular NDEF types we're interested in receiving into our application. In our example we have included a list of three URI values, separated by commas. Sp means Smart Poster, T means Text and U means URI. You must use the syntax exactly as shown here.

  

The “<action>” tag has the value of “bb.action.OPEN”. This means that when the invocation framework is presented with an object of the MIME types we’ve registered for, our application will be asked to “OPEN” the associated data. That is, it will be presented with the contents of the NDEF tag that has just been detected and read by the NFC stack in the handset. Our application is then responsible for parsing the content and doing something with it; in our case we simply display it on the screen.

 

 

Step 2 – Create a bb::system::InvokeManager object

 

 

    
bb::system::InvokeManager * _invokeManager;
......
_invokeManager = new bb::system::InvokeManager();

 

  Figure 14 - Creating an InvokeManager object

 

The APIs include the InvokeManager class. This is a useful class for working with the invocation framework and we use it in NfcTool to make reading (in fact *receiving*) NFC tag data really easy. So one of our first steps, in the MainMenu.cpp class is to create an instance of this class.

 

Step 3 – Connect InvokeManager invoked signal to your slot

 

QObject::connect(_invokeManager, SIGNAL(invoked(const bb::system::InvokeRequest&)), this, SLOT(receivedInvokeRequest(const bb::system::InvokeRequest&)));

 Figure 15 - Connecting InvokeManager signal/slot 

 

InvokeManager uses Qt signals and slots. In NfcTool we connect the "invoked" signal to a method calls receivedInvokeRequest in the MainMenu class which we have designated as a slot. As you can see, it takes a parameter of type InvokeRequest.

 

Step 4 - Process NDEF messages in InvokeRequest objects

 

Receive an InvokeRequest object from the InvokeManager via a call to our slot method;
Extract the request data from the InvokeRequest object
Interpret the request data as an NDEF message
for each NDEF record in this NDEF message {
	extract NDEF record attributes according to the record type;
}

 Figure 16 - Tag reading event loop in pseudo code

 

 

The final step involves extracting the NDEF data from the InvokeRequest object we received from the invocation framework. Figure 16 expresses the basic algorithm in pseudo code. We’ll take a look at an example C++ implementation next.

 

 

void MainMenu::receivedInvokeRequest(const bb::system::InvokeRequest& request) {

	QByteArray data = request.data();
	if (request.mimeType().compare("application/vnd.rim.nfc.ndef") == 0) {
		emit launchEventLog();
		_nfcManager = NfcManager::getInstance();
		_nfcManager->handleTagReadInvocation(data);
	}
........
}

 

 Figure 17 – Slot method which receives InvokeRequest from the framework

 

 

Figure 17 shows the relevant parts of our receivedInvokeRequest method. This method is the slot connected to the InvokeManager's "invoked" signal, so whenever the invocation framework has data we're interested in, it calls this method and passes the data as an InvokeRequest object. Our job is to extract the contents of the InvokeRequest object and transform it into NDEF data from the tag that was read. As you can see from the code fragment, we proceed by extracting the request's data payload, checking the mime type and assuming it's what we expect it to be, we then call another method to handle the transformation of our payload byte array into an NDEF message containing one or more NDEF records. Ultimately, this takes place in the NfcWorker class. Let's take a look at the primary aspects of this.

 

 

void NfcWorker::handleTagReadInvocation(const QByteArray data) {

	nfc_ndef_message_t *ndefMessage;

	CHECK( nfc_create_ndef_message_from_bytes(reinterpret_cast<const uchar_t *>(data.data()), data.length(), &ndefMessage));

	// cause event log to be displayed now

	StateManager* state_mgr = StateManager::getInstance();
	emit clearMessages();
	if (!state_mgr->isEventLogShowing()) {
		emit read_selected();
	}

	parseNdefMessage(ndefMessage);
	CHECK(nfc_delete_ndef_message(ndefMessage, true));
}

void NfcWorker::parseNdefMessage(nfc_ndef_message_t *ndefMessage) {

	int ndefMsgCount = 0;
	ndefMsgCount = 1;

	for (int ndefMsgIndex = 0; ndefMsgIndex < ndefMsgCount; ++ndefMsgIndex) {
		unsigned int ndefRecordCount = 0;
		CHECK(nfc_get_ndef_record_count(ndefMessage, &ndefRecordCount));

		for (unsigned int ndefRecordIndex = 0; ndefRecordIndex < ndefRecordCount; ++ndefRecordIndex) {
			nfc_ndef_record_t *ndefRecord;
			CHECK( nfc_get_ndef_record(ndefMessage, ndefRecordIndex, &ndefRecord));
			uchar_t* ndefRecordPayloadData;
			size_t ndefRecordPayloadLength;
			CHECK(nfc_get_ndef_record_payload(ndefRecord, &ndefRecordPayloadData, &ndefRecordPayloadLength));

			tnf_type_t ndefRecordTnf;
			CHECK(nfc_get_ndef_record_tnf(ndefRecord, &ndefRecordTnf));
			char *ndefRecordNdefType;
			CHECK(nfc_get_ndef_record_type(ndefRecord, &ndefRecordNdefType));
			char *ndefRecordIdentifier;
			CHECK(nfc_get_ndef_record_id(ndefRecord, &ndefRecordIdentifier));

			QString ndefRecordPayloadAsHex =
					QString::fromAscii(reinterpret_cast<const char *>(ndefRecordPayloadData), ndefRecordPayloadLength).toAscii().toHex();
			QByteArray payLoadData = QByteArray::fromRawData(reinterpret_cast<const char *>(ndefRecordPayloadData), ndefRecordPayloadLength);

			emit message(QString("TNF: %1").arg(ndefRecordTnf));
			emit message(QString("Type: %1").arg(ndefRecordNdefType));
			emit message(QString("Id: %1").arg(ndefRecordIdentifier));

			if (strcmp(ndefRecordNdefType, Settings::NfcRtdSmartPoster) == 0) {
				emit message(QString("Smart Poster Tag"));

				char *utf_title;
				char *found_lang;
				char *uri;
				nfc_ndef_rtd_encoding_t rtd_encoding;
				rtd_encoding = UTF_8;

				CHECK( nfc_get_sp_title(ndefRecord, Settings::LANG_EN, &utf_title, &found_lang, &rtd_encoding, true));

				CHECK(nfc_get_sp_uri(ndefRecord, &uri));
				emit message(QString("Language: %1").arg(found_lang));
				emit message(QString("Title: %1").arg(utf_title));
				emit message(QString("URI: %1").arg(uri));
				free(utf_title);
				free(uri);
			} else if (strcmp(ndefRecordNdefType, Settings::NfcRtdUri) == 0) {
				emit message(QString("URI Tag"));

				QString uri;
				uchar_t uriType;
				int uriLen;

				uriType = ndefRecordPayloadData[0];
				uriLen = payLoadData.length() - 1;

				uri = QString::fromUtf8(payLoadData.right(uriLen).constData(), uriLen);

				if (uriType == Settings::NfcRtdUriPrefixHttpWww) {
					uri.prepend(QString("http://www."));

				} else if (uriType == Settings::NfcRtdUriPrefixHttpsWww) {
					uri.prepend(QString("https://www."));

				} else if (uriType == Settings::NfcRtdUriPrefixHttp) {
					uri.prepend(QString("http://"));

				} else if (uriType == Settings::NfcRtdUriPrefixHttps) {
					uri.prepend(QString("https://"));

				} else if (uriType != Settings::NfcRtdUriPrefixNone) {
					emit message(QString("URI Prefix %1 not implemented").arg(uriType));
				}

				emit message(QString("URI: %1").arg(uri));

			} else if (strcmp(ndefRecordNdefType, Settings::NfcRtdText) == 0) {

				emit message(QString("Text Tag"));

				QString text;
				QString language;
				int languageLen;
				int textLen;

				languageLen = ndefRecordPayloadData[0];
				textLen = payLoadData.length() - (languageLen + 1);

				language = QString::fromUtf8(payLoadData.mid(1, languageLen).constData(), languageLen);
				text = QString::fromUtf8(payLoadData.right(textLen).constData(), textLen);

				emit message(QString("Language: %1").arg(language));
				emit message(QString("Text: %1").arg(text));
			}

		}
	}
}

 

 Figure 18 – Processing NFC NDEF data from an InvokeRequest object

 

Figure 18 shows the steps involved in processing the NFC tag data which was passed to us inside an InvokeRequest object from the invocation framework. Per the pseudo code description in Figure 16, we work our way from a byte array which we extracted from the InvokeRequest object that the invocation framework sent us, then through the NDEF messages, each of which contains one or more NDEF records and for each record, we extract the record’s attributes according to its type. Your code will differ according to whatever it is you’re doing, but hopefully this will get your started. Take a look at the nfc/nfc.h, nfc/nfc_ndef.h and nfc/nfc_types.h header files for the full list of NFC tag related functions in the API.

  

 

Writing NFC Tags

 

 

Now that we understand how to read an NDEF tag let’s consider how to write a tag. There are four common tag types that are useful to learn how to write since they demonstrate all of the NFC C++ APIs that you will need to construct others. They are:

 

 

  • A URI Tag – this consists of a single URI such as http://www.rim.com
  • A Text Tag—this consists of a single string of readable text such as “Hello, NFC!”
  • A Smart Poster Tag – this consists of a URI, like in a URI tag, and one or more text annotations describing the URI that can be in different languages
  • A Custom Tag – this consists of a unique domain (usually a DNS domain like “my.domain.com” ) that identifies the tag as being specific to this organization and a type ( like “myownrecord” ). Together these identify a unique namespace to prevent clashes with tags from other organizations. Lastly, an arbitrary payload completely determined by the use you wish to make of the tag.

 

 

The process for writing a tag can be described by the following 4 step recipe:

 

Recipe for Writing a Tag

 

tag writing recipe.png

Figure 19 - Recipe for writing NFC tags

 

 

Step 1 – Initialize the BlackBerry Platform Services (BPS) library


When writing a tag, we use the BlackBerry Platform Services (BPS) APIs. Our first job therefore is to initialize BPS. Initializing the BPS library requires a single function call only as you can see from Figure 20. Note that you must remember to include the bps/bps.h header file in your class. This function must be the first BPS function you call for a thread when you want to use BPS library functions. 


Step 2 – Request NFC events from the BPS

 

#include <bps/bps.h>
#include <bps/nfc_bps.h> ...... rc = bps_initialize();
rc = nfc_request_events();

 Figure 20 - Requesting NFC events

 

Similarly, requesting the delivery of NFC events via BPS is also achieved with a single function call. Note that you need to include bps/nfc_bps.h for this function to be available.

 

The last step is the similar to that for reading an NDEF tag. The only difference is that we need to detect and process a BPS NFC event type of NFC_TAG_READWRITE_EVENT which represents the presentation of a tag to the handset.

 

The delivery of this event uses the same BPS framework used to deliver Invocation Framework events and the processing is almost identical. This has already been explained earlier in this article so let’s concentrate on the item that is different: Step 3.
 
In preparing to read a tag, we registered with the Invocation Framework for specific NDEF Message types such as URI, Text and Smart Poster. In preparing to write, a tag we’re only interested in being notified when a suitable target is presented to the device since we’re going to write new NDEF data to it.

  

You need to make a decision here. There are three types of NFC Target detection events that we can register for:

 

  • ISO_1444_3 – in this case, you will receive notification when a target is presented to the device that uses the ISO 1444-3 protocol and you wish to interact with the target using the protocol that’s defined at this level.
  • ISO_1444_4 – in this case, you receive notification when a target is presented to the device that uses the ISO 1444-4 protocol and you wish to interact with the target using the protocol that’s defined at this level. This would typically be by constructing APDUs, sending them to the tag and receiving APDU’s in response.
  • NDEF_TAG – in this case, you receive notification when a target is presented to the device that has been formatted for use as an NDEF tag and you wish to interact with the target using NDEF messages.

 

In the case of this application, we’re interested only in NDEF formatted tags so the simplest approach is to select NDEF_TAG as the target detection event in which we’re interested. That’s not to say that you couldn’t use, say, ISO_1444_4. If you did you would have to be prepared to test each detection event yourself to determine whether there was an NDEF structure on the tag that could be written to. It’s a case of whether you want the NFC framework to check this for you or whether you want to do it yourself. In this case, we use NDEF_TAG and let the framework do the work for us.

 

Right, let’s look at the four cases of URI, Text, Smart Poster and Custom Tag writing in turn. Since the process is very similar in each case, I’ll spend more time in the first example and then highlight any significant differences in the other cases.

 

Writing a URI Tag

 

Here’s what’s presented to the user when he wishes to write a URI tag using NFC Tool.

 

IMG_00000018.png

Figure 20 - NFC Tool : Writing a URI tag

 

Preparation for Writing a URI Tag

 

When the user identifies that he wishes to write a URI NDEF Message to a tag when it’s presented to the device the function:  prepareToWriteNdefUriTag()  of the NfcWorker class is called in response to the “Write” button in the UI to prepare to write the URI when a tag is presented at a later time.

 

void NfcWorker::prepareToWriteNdefUriTag(QString uri) {

[...]

	emit message(QString("Preparing to write URI: %1").arg(uri));

	_ndefUri = uri;

	CHECK(nfc_register_tag_readerwriter(NDEF_TAG));
}

 Figure 21 - Preparing to write a URI tag

 

Some code has been removed from the actual example to highlight the three main points.

 

  • Firstly, a signal called message() is emitted that explains what is happening. In fact this SIGNAL will be connected to a SLOT in the event log class to show log this event on the screen;
  • Secondly, the actual uri to be written is saved to be used later when a tag is presented;
  • And finally nfc_register_tag_readerwriter() is used to register for NFC events. The CHECK() macro is just a convenience to be able to handle the return code from the NFC API calls.

 

Handling the Target Detected Event for Writing a URI Tag

 

Once the application has been registered to receive NDEF Target detection events these events will start to be presented in the main listen handler of the application in exactly the same way as already described in the section on reading an NDEF tag.

 

These are handled in the function called handleNfcWriteUriTagEvent(), the highlights of which are shown below.

 

void NfcWorker::handleNfcWriteUriTagEvent(bps_event_t *event) {
[...]
	nfc_event_t *nfcEvent;
	nfc_target_t* target;
	nfc_ndef_record_t* myNdefRecord;
	nfc_ndef_message_t* myNdefMessage;
	if (NFC_TAG_READWRITE_EVENT == bps_event_get_code(event)) {
           	rc = nfc_get_nfc_event(event, &nfcEvent);
              	rc = nfc_get_target(nfcEvent, &target);

		myNdefRecord = makeUriRecord(
                              Settings::NfcRtdUriPrefixNone, _ndefUri);
		CHECK(nfc_create_ndef_message(&myNdefMessage));
		CHECK(nfc_add_ndef_record(myNdefMessage, myNdefRecord));
		CHECK(nfc_write_ndef_message_to_tag(target, 
                      myNdefMessage, false));
		CHECK(nfc_delete_ndef_message(myNdefMessage, true));

		emit message(QString("Tag Type Written URI: %1").arg(_ndefUri));
	}
[...]
}

  Figure 22 - Handling the target detected event

  

The main points to note are:

  • As a defensive measure, the event type that is being handled is check to ensure it’s of the correct type, namely: NFC_TAG_READWRITE_EVENT. We’re assured that an NDEF target has been presented that can be written to.
  • An NDEF Record is constructed using a function called makeUriRecord() which is shown below.
  • A pointer to an empty NDEF Message is obtained from the framework into which the URI NDEF Record is inserted.
  • The NDEF Message is written to the tag and then deleted.

 

nfc_ndef_record_t* NfcWorker::makeUriRecord(uchar_t prefix, QString uri) {
	nfc_ndef_record_t* record = 0;
	int len = uri.length();
	uchar_t payload[len + 1];
	payload[0] = prefix;
	memcpy(&payload[1], uri.toUtf8().constData(), len);
	CHECK( nfc_create_ndef_record(NDEF_TNF_WELL_KNOWN, 
                                     “U”, payload, len+1, 0, &record));
	return record;
}

 Figure 23 - Creating the URI type NDEF record

 

The makeUriRecord()  function uses a standard NFC API function to create an NDEF Record of TNF “Well Known” type and value “U”, and constructs a payload of two parts:

  • The URI that will be used
  • And a prefix byte that allow common prefixes such as http://www. to be encoded efficiently in the scarce space on a small tag.

Successful writing of the URI tag is communicated back to the user in the event log screen.

 

IMG_00000020.png

Figure 24 - Event log during URI writing process

 

 

Writing a Text Tag

 

Writing a Text NDEF message to an NDEF tag is almost identical to writing a URI NDEF Message. The only significant difference is that we use a different function to build the NDEF Text record. The makeTextRecord()  function uses a standard NFC API function to create an NDEF Record of TNF “Well Known” type and value “T”, and constructs a payload of two parts:

 

  • The Text value that will be used
  • And a language field consisting of a status byte that encodes both the length of the language code ( e.g. “en” for “English” would be length 2 ) and an indication of the encoding of the Text value. In this case it’s UTF-8.

 

nfc_ndef_record_t* NfcWorker::makeTextRecord(QString language, QString text) {
[...]
	nfc_ndef_record_t* record = 0;
	int textLen = text.length();
	int languageLen = language.length();
	int totalLen = textLen + languageLen + 1;

	uchar_t payload[totalLen];

	int offset = 0;

      // set status byte. Since text is UTF-8 and RFU must be 0, bits 7 and 6
      // are 0 and therefore the entire status byte value is the language code length
	
	payload[offset] = languageLen; // including encoding indication for UTF-8
	offset += 1;
	memcpy(&payload[offset], language.toUtf8().constData(), languageLen);
	offset += languageLen;
	memcpy(&payload[offset], text.toUtf8().constData(), textLen);
	CHECK( nfc_create_ndef_record(NDEF_TNF_WELL_KNOWN, 
                                     “T”, payload, 
                                     totalLen, 0, &record));
	return record;
}

 Figure 25 - creating a Text type NDEF record

 

Writing a Smart Poster Tag

 

Writing a Smart Poster NDEF Message to a tag may appear to be a more complex task than writing a URI or a Text tag since it will contain multiple NDEF records. However, the NFC NDEF API makes this a much simpler task by providing a number of functions that allow you to construct a Smart Poster tag without needing to know about the detailed structure of the tag layout itself. In fact we only need to look at the differences in the handleNfcWriteSpTagEvent() method in the sample application to understand how to do it.

 

The code fragment below shows the main aspects of how to build the Smart Poster Tag. In essence:

 

  • Create an empty NDEF Record of “Well Known” type with value “Sp” for a Smart Poster
  • Use nfc_set_sp_uri() to set the value of the URI associated with the Smart Poster NDEF Message into the empty NDEF Record we’ve just created.
  • Use nfc_add_sp_title() to add the text to be associated with the URI in the language “en” for English. Notice that you can add additional text records each for a different language to describe the single URI record.
  • Create and empty NDEF Message using nfc_create_ndef_messag() and add the NDEF record we’ve been populating with URI and Text information to that NDEF Message.
  • Then just write the NDEF Message to the tag as before.

That’s all there is to it! Easy!

 

void NfcWorker::handleNfcWriteSpTagEvent(bps_event_t *event) {
[...]
	uint16_t code = bps_event_get_code(event);
	nfc_target_t* target;
	nfc_ndef_record_t* spNdefRecord;
	nfc_ndef_message_t* myNdefMessage;

	if (NFC_TAG_READWRITE_EVENT == code) {

		CHECK(nfc_get_target(event, &target));

		spNdefRecord = makeSpRecord();

		CHECK(nfc_create_ndef_message(&myNdefMessage));
		CHECK(nfc_set_sp_uri(spNdefRecord, _ndefSpUri.toUtf8().constData()));
		CHECK( nfc_add_sp_title(spNdefRecord, “en”,
                                     _ndefSpText.toUtf8().constData(), false));
		CHECK(nfc_add_ndef_record(myNdefMessage, spNdefRecord));
		CHECK(nfc_write_ndef_message_to_tag(target, myNdefMessage, false));
		CHECK(nfc_delete_ndef_message(myNdefMessage, true));

		emit message(QString("Tag Type Sp Written: %1 %2").arg(_ndefSpUri)
                                                               .arg(_ndefSpText));
	}
[...]
}

nfc_ndef_record_t* NfcWorker::makeSpRecord() {
	nfc_ndef_record_t* record = 0;
	uchar_t payload[0];
	CHECK( nfc_create_ndef_record(NDEF_TNF_WELL_KNOWN, 
                                    “Sp”, payload, 0, 0, &record));
	return record;
}

 Figure 26 - Creating a Smart Poster NDEF record

  

Writing a Custom Tag

 

A Custom tag comprises three sets of data encoded into an NDEF message as described previously. The sample application presents the following information to the user when he wants to write such a message to a tag.

 

IMG_00000033.png

Figure 27 - Writing a custom tag

 

The process is very similar to the process for writing Text and URI tags since there aren’t any functions that allow you to build one simply like in the case of the Smart Poster Tag. In fact the only difference is in the function makeCustomRecord() used to build the NDEF Record from the Domain, Type and Text attributes.

 

nfc_ndef_record_t* NfcWorker::makeCustomRecord(QString domain, 
                                               QString type, QString text) {
[...]
	nfc_ndef_record_t* record = 0;

	int textLen = text.length();
	QString domain_plus_type = domain.append(":");
	domain_plus_type = domain_plus_type.append(type);
	int totalLen = textLen;

	uchar_t payload[totalLen];

	int offset = 0;
	memcpy(&payload[offset], text.toUtf8().constData(), textLen);

	CHECK( nfc_create_ndef_record(NDEF_TNF_EXTERNAL,
                                     domain_plus_type.toUtf8().constData(), 
                                     payload, totalLen, 0, &record));
	return record;
}

 Figure 28 - Creating a custom NDEF record

 

The key points to note are:

 

  • A Custom NDEF Record uses a TNF Type of “External” rather than the type “Well Known” as in the case of the URI, Text and Smart Poster tags.
  • The actual value of the record type associated with the TNF "External" is a concatenation of the domain value and the private type using a semi-colon as the join character. So, if my domain was: "foo.com" and my private type was: "splat", then the record type would be: "foo.com:splat".
  • The payload of the NDEF record just contains the content that the end user specified.

 

Summary

 

We hope that this article helps you exploit the BlackBerry 10 NFC APIs for tag reading and writing and that our exploration of the more general design and implementation aspects of our sample application “NFC Tool” are useful in helping you get started with BlackBerry 10 application development using C++. Qt and Cascades.

 

You can download NFC Tool, including its full source code from:

 

https://github.com/blackberry/Cascades-Community-Samples

 

NFC Tool as written for the BlackBerry 10 "Dev Alpha" device and requires the following versions of the NDK and device software to build and run:

  • BlackBerry 10® Native SDK 10.0.9
  • BlackBerry® Dev Alpha Device Software 10.0.9

You can find details of other NFC related articles and sample applications written by Martin and John at:

 

NFC Article and Code Index

 

You can contact Martin or John either through the BlackBerry support forums or through Twitter®:

 

  Support Forum ID Twitter
Martin mwoolley @mdwrim
John jomurray @jcmrim

 

Users Online
Currently online: 40 members 1,002 guests
Please welcome our newest community members: