How to use NFC in Qt/QML application?

Qt QML development
2021-03-09
12 minutes
NFC in Qt/QML application

Have you ever wondered how to use NFC in Qt/QML application? This blog post will give you a complex overview of the concept of near-field communication technology with examples of its usage in real life. Then you will discover how to implement NFC reading and writing in Qt QML application. If you need any help with this topic, you can check our Mobile development services.

 

What is NFC?

NFC stands for near–field communication and it provides a way to transfer data wirelessly. It evolved from RFID technology which used electromagnetic induction to transmit data. You could have a RFID chip in hand and probably you did not even know about it, as they are often used in form of pendant allowing to access for e.g. office space.

NFC, just like other wireless transfer technologies (Bluetooth, Wi-Fi), take advance of radio waves. However, the main advantage of NFC is the fact that passive devices do not require additional power supply. When there is a passive device in the discovery range of active device, the electromagnetic field created by active NFC devices powers the passive one. Also unlike Bluetooth, near-field communication does not require to first pair with other device.

As the name implies, NFC allows to transfer data within short-range which is roughly 10 centimeters (around 4 inches). This short range is often posed as an advantage, because of security reasons.

Most of today’s smartphones are equipped with NFC antennas being active NFC devices. What does this mean? Active devices allow us to both send and receive data by communicating with each other or with passive devices. Passive devices, on the other hand, can connect to neither active nor passive devices. This type of devices are usually used as interactive tags to hold some data.

3 NFC usage examples

Take a look to find out how NFC technology is used in daily life. Get inspired on how you can benefit from using NFC in your app.

Mobile payments with NFC

Both Android and iPhone devices employ NFC to perform secure, wireless payments which have become a common way to pay for goods in many countries across the world. With NFC you can use your phone just like a wireless credit card thanks to a built-in NFC antenna.

Many digital wallet platforms just like Google Pay, Apple Pay or Samsung Pay take advantage of NFC allowing you to use them together with this technology.

NFC

 

Sharing between two active devices with NFC

On Android you have the possibility to transfer data from one smartphone to another just by touching both devices back-to-back. After confirmation data are sent rapidly.

NFC tags

As mentioned, passive NFC devices do not need to have their own power source and they can be programmed to perform a wide range of tasks and to keep various kinds of data. Popular, yet simple usage of NFC tag is putting one next to bed and programming it to automatically enable silent mode when touched by phone.

Of course those are only few examples of how NFC technology is used. Number of possible usages of NFC tags is infinitive. Continue reading to take a look at usage implementation of NFC chips in Qt QML/application.

How to implement NFC in your Qt/QML Application?

Among many features shipped within the feature-rich Qt Framework, there is a Qt NFC module that allows to communicate over NFC with both active and passive devices. Currently the module supports Linux using Neard and Android. Let’s see how to implement NFC in your Qt QML application.

First let’s start with an idea for mobile application that will take advantage of NFC tags. I want to have an application that will ease my cook’s life. It will allow users to put a dish name on a NFC tag together with time indicating how long cooking or baking takes. Then when such a tag is touched, the application will start a timer counting down from that time and then a sound alarm will be triggered. This way you will never burn your dishes.

Presented demo is simple, but good enough to show real adoption of NFC in Qt app, including:

  • NFC tags detection;
  • NFC tags writing;
  • NFC tags reading;
  • Exposing C++ objects and classes to QML;

This blog post will not cover UI part done in QML, so feel free to look at full code source that can be found on Scythe Studio’s Github profile.

NFC tags detection

Let’s start with the most important part of NFC implementation – detection. We created NFCManager class that is supposed to be responsible for all the NFC actions like detecting, reading and writing. It is exposed to QML in an easy way thanks to QML_ELEMENT macro that was introduced in Qt 5.15.

Among other members and methods there is QNearFieldManager pointer that is crucial to control NFC targets detection. 

 

// directives, forward declarations
// Record struct declaration

class NFCManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool hasTagInRange READ hasTagInRange NOTIFY hasTagInRangeChanged)
    Q_PROPERTY(ActionType actionType READ actionType WRITE setActionType NOTIFY actionTypeChanged)
    Q_PROPERTY(Record record READ record NOTIFY recordChanged)
    QML_ELEMENT

public:
    explicit NFCManager(QObject *parent = nullptr);

    enum ActionType
    {
        None = 0,
        Reading,
        Writing
    };
    Q_ENUM(ActionType)

    bool hasTagInRange() const;
    ActionType actionType() const;
    Record record() const;

 

public slots:
    void startReading();
    void stopDetecting();
    void saveRecord(const QString &dishName, int seconds);

signals:
    void hasTagInRangeChanged(bool hasTagInRange);
    void actionTypeChanged(ActionType actionType);
    void recordChanged(const Record &record);

    void wroteSuccessfully();
    void nfcError(const QString &error);

private slots:
    void setActionType(ActionType actionType);
    void setHasTagInRange(bool hasTagInRange);

    void onTargetDetected(QNearFieldTarget *target);
    void onTargetLost(QNearFieldTarget *target);

    void onNdefMessageRead(const QNdefMessage &message);
    void onNdefMessageWritten();
    void handleTargetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id);

private:
    bool m_hasTagInRange = false;
    ActionType m_actionType = ActionType::None;

    Record m_record;
    QNearFieldManager *m_manager;
    QNearFieldTarget::RequestId m_request;

};

In class constructor you need to initialize the QNearFieldManager instance and connect appropriate signals to be able to handle NFC events. The signals that we are interested in are `QNearFieldManager::targetDetected` and `QNearFieldManager::targetLost`. As names indicate, they are emitted when the target device (either active or passive) enters or leaves the detection range. 

 

NFCManager::NFCManager(QObject *parent)
    : QObject(parent)
    , m_manager(new QNearFieldManager(this)
{
    connect(m_manager, &QNearFieldManager::targetDetected,
            this, &NFCManager::onTargetDetected);

    connect(m_manager, &QNearFieldManager::targetLost,
            this, &NFCManager::onTargetLost);
}  

Signals will not be emitted unless we ask QNearFieldManager instance to start detection. To do that you need to call `QNearFieldManager::startTargetDetection` method on the object instance. Before starting detection set target access mode to tell QNearFieldManager what you are going to do with the target device – either read or write. For reading you need to set the target access mode to `QNearFieldManager::NdefReadTargetAccess` and for writing use `QNearFieldManager::NdefWriteTargetAccess`.

 

void NFCManager::startReading()
{
    setActionType(ActionType::Reading);
    m_manager->setTargetAccessModes(QNearFieldManager::NdefReadTargetAccess);
    m_manager->startTargetDetection();
}

void NFCManager::stopDetecting()
{
    setActionType(ActionType::None);
    m_manager->setTargetAccessModes(QNearFieldManager::NoTargetAccess);
    m_manager->stopTargetDetection();
}

The code snippet above shows you how to start detection for reading and how to stop detection regardless of current access mode. There is also `NFCManager::setActionType()` method used, but it is not part of Qt NFC API. It is used in this demo to indicate which action (Reading, Writing, None) is being performed now.

NFC Tags Writing

Once the detection part is done we need to implement NFCManager behavior when the tag enters and leaves range. First let’s implement behavior for writing tags to put some data on them. For this demo I used NFC tag stickers.

NFC

You may notice that the NFCManager has a `Record m_record` member. This variable is used to keep data to be written on tag or data read from a tag. `Record` is a structure, so let’s take a look at its declaration.

 

struct Record {
    Q_GADGET
    Q_PROPERTY(int seconds MEMBER seconds)
    Q_PROPERTY(QString dishName MEMBER dishName)

public:
    int seconds = 0;
    QString dishName = "";
    bool parseNdefMessage(const QNdefNfcTextRecord &record);
    QNdefMessage generateNdefMessage() const;
};
Q_DECLARE_METATYPE(Record)

The structure has two fields responsible for keeping the name of the dish and cooking time in seconds. To write Ndef messages on target device you need to generate QNdefMessage using data kept in a structure. QNdefMessage is a collection of QNdefRecords that are single parts of a message. The best way to implement NFC in Qt/QML application is to make a derived class based on QNdefRecord. To keep this demo simple let’s assume that we will keep only one message with one record on one tag and the content of the tag will be a JSON string. 

 

QNdefMessage Record::generateNdefMessage() const
{
    if (dishName.isEmpty() || seconds <= 0) {
        return QNdefMessage();
    }

    QNdefMessage message; 

    QVariantMap recordMap{};
    recordMap[DISHNAME] = dishName;
    recordMap[SECONDS] = seconds;

    const QJsonDocument &doc = QJsonDocument::fromVariant(recordMap); 

    QNdefNfcTextRecord record;
    record.setEncoding(QNdefNfcTextRecord::Utf8);
    record.setText(doc.toJson());
    message.append(record);

    return message;
}

 

So this is how you could implement a method for generating QNdefMessage from your own structure. First JSON document is created and then it is set as text for QNdefNfcTextRecord variable. This record is then put in to QNdefMessage. 

 

void NFCManager::saveRecord(const QString &dishName, int seconds)
{
    m_record.dishName = dishName;
    m_record.seconds = seconds;

    setActionType(ActionType::Writing);
    m_manager->setTargetAccessModes(QNearFieldManager::NdefWriteTargetAccess);
    m_manager->startTargetDetection();
}

As a next step we’ll take a look at `NFCManager::saveRecord` implementation that is pretty similar to `NFCManager::startReading` method, but it has two extra parameters to save name of the dish and cooking time into m_record. Also it has different target access mode. This method is called from QML part with parameters fetched from the user’s input

access mode

It’s time to implement an actual behavior on target detection. For this purpose we will take use of internal m_actionType member to decide what to do in particular situation as `QNearFieldManager::targetDetected` signal is emitted regardless of actual target access mode.

 

void NFCManager::onTargetDetected(QNearFieldTarget *target)
{
    setHasTagInRange(true);

    switch (m_actionType) {
    case None:
        break;
    case Reading:
        // reading ...
        break;
    case Writing:
        connect(target, &QNearFieldTarget::ndefMessagesWritten, this, &NFCManager::onNdefMessageWritten);
        connect(target, &QNearFieldTarget::error, this, &NFCManager::handleTargetError);

        m_request = target->writeNdefMessages(QList<QNdefMessage>() << m_record.generateNdefMessage());

        if (!m_request.isValid()) {
            handleTargetError(QNearFieldTarget::NdefWriteError, m_request);
        }
        break;
    }
}

From this point you can see that it is not complicated at all. All you need to do is to call `QNearFieldTarget::writeNdefMessages` on detected target with list of messages to be written on target as a parameter. For this demo list has only one single item that is a message generated from Record structure. 

To prevent silent errors you should validate the result of calling the method saved in m_request class member and connect it to `QNearFieldTarget::error`.

 

void NFCManager::onNdefMessageWritten()
{
    stopDetecting();
    m_request = QNearFieldTarget::RequestId();

    emit wroteSuccessfully();
}

However remember to connect to target’s `QNearFieldTarget::ndefMessagesWritten` signal in order to handle successful message writing. In such method feel free to stop detecting, empty m_request and emit a signal indicating that all worked as it should.

That’s all for writing to NFC tags in Qt app. Now call `NFCManager::saveRecord` method on NFCManager instance exposed to QML and touch a tag with your phone. Dish name and cooking time should be written successfully on a tag.

cooking time

NFC Tags Reading

We already have some data on tag – now it’s time for reading. In `NFCManager::onTargetDetected` definition, in proper switch case (previously commented) call `QNearFieldTarget::readNdefMessages()` on detected target and save a result in m_request class member. However, before doing this connect to `QNearFieldTarget::ndefMessageRead` in order to get content of the tag. Remember about handling errors cases.

 

        connect(target, &QNearFieldTarget::ndefMessageRead, this, &NFCManager::onNdefMessageRead);
        connect(target, &QNearFieldTarget::error, this, &NFCManager::handleTargetError);

        m_request = target->readNdefMessages();
        if (!m_request.isValid()) {
            handleTargetError(QNearFieldTarget::NdefReadError, m_request);
        }

For NFC tags reading `NFCManager::onNdefMessageRead` plays first fiddle. It comes with QNdefMessage as a parameter so first we need to iterate over records in that message and look for records of type QNdefNfcTextRecord. As you used this type to write the message, it’s the only type we are interested in. If a record of such type was parsed successfully by `Record::parseNdefMessage` method, let’s finish iteration, stop detection and emit a signal indicating that record changed. Then you can handle this signal in QML. 

That is all you need to know. To finish this tutorial formally let’s take a look how you could implement `Record::parseNdefMessage`.

 

bool Record::parseNdefMessage(const QNdefNfcTextRecord &record)
{
    const QJsonDocument &doc = QJsonDocument::fromJson(record.text().toUtf8());
    const QJsonObject &recordObject = doc.object();
 
    if (!recordObject.contains(DISHNAME) || !recordObject.contains(DISHNAME)) {
        return false;
    }

    dishName = recordObject[DISHNAME].toString();
    seconds = recordObject[SECONDS].toInt();

    return true;
}

As you see there is nothing exceptionaly difficult here. QNdefNfcTextRecord parameter has a text method that returns saved string. From that point you can parse it as json as usual. 

NFC on phone

How to use NFC in Qt/QML application – summary

Thanks for getting familiar with NFC in Qt with us. Thanks to this blog post you should:

  • Understand what is Near Field Communication technology;
  • Know what is the usage of NFC and how you can benefit from this;
  • Know how to implement detecting, writing and reading NFC targets in Qt/QML application;
Scythe-Studio - Chief Executive Officer

Łukasz Kosiński Chief Executive Officer

Need Qt QML development services?

service partner

Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.

Discover our capabilities

Latest posts

[ 134 ]