Ndef compatible tag types
The NFC Forum has mandated the support of five different tag types to be
operable with NFC devices. The same is required on operating systems, such
as Android.
In addition to that, the MIFARE Standard specifies a way
for NDEF to work on top of the older MIFARE Standard, which may
be optionally supported by implementers.
A note about the NDEF mapping can be found here:
MIFARE Classic as NFC Type MIFARE Classic Tag.
- NFC Forum Type 1: This tag is based on the ISO/IEC 14443-3A
(NFC-A). The tags are rewritable and can be
configured to become read-only. Memory size can be between `96` bytes and
`2` Kbytes. Communication speed is `106` kbit/s. In contrast to all other
types, these tags have no anti-collision protection for dealing with multiple
tags within the NFC field. - NFC Forum Type 2: This tag is based on the
ISO/IEC 14443-3A (NFC-A). The tags are rewritable and can be configured
to become read-only. Memory size can be between `48` bytes and `2` Kbytes.
Communication speed is `106` kbit/s. - NFC Forum Type 3: This tag is based on the Japanese Industrial
Standard (JIS) X 6319-4 (ISO/IEC 18092), commonly known as FeliCa. The tags are
preconfigured to be either rewritable or read-only. Memory is `2` kbytes.
Communication speed is `212` kbit/s or `424` kbit/s. - NFC Forum Type 4: This tag is based on the ISO/IEC 14443-4 A/B
(NFC A, NFC B) and thus supports either NFC-A or NFC-B
for communication. On top of that the tag may optionally support ISO-DEP
(Data Exchange Protocol defined in ISO/IEC 14443 (ISO/IEC 14443-4:2008
Part 4: Transmission protocol). The tags are preconfigured
to be either rewritable or read-only. Variable memory, up to `32` kbytes.
Supports three different communication speeds `106` or `212` or
`424` kbit/s. - NFC Forum Type 5: This tag is based on ISO/IEC 15693 (NFC-V) and
allows reading and writing an NDEF message on an ISO/IEC 15693 RF tag
that is accessible by long range RFID readers as well. The NFC communication
is limited to short distance and may use the Active Communication Mode of
ISO/IEC 18092 where the sending peer generates the field which balances
power consumption and improves link stability. Variable memory, up to `64` kbytes.
Communication speed is `26.48` kbit/s. - MIFARE Standard: This tag, often sold under the brand names MIFARE
Classic or MIFARE Mini, is based on the ISO/IEC 14443-3A (also known as NFC-A,
as defined in ISO/IEC 14443-3:2021, Part 3: Initialization and anticollision).
The tags are rewritable and can be configured to become read-only. Memory size
can be between `320` and `4` kbytes. Communication speed is `106` kbit/s.MIFARE Standard is not an NFC Forum type and can only be read by devices
using NXP hardware. Support for reading and writing to tags based on the
MIFARE Standard is thus non-nominative, but the type is included
due to the popularity and use in legacy systems.
In addition to data types standardized for NDEF records by the NFC
Forum, many commercial products such as bus cards, door openers may be based
on the MIFARE Standard which requires specific NFC chips (same vendor of
card and reader) in order to function.
Read and write a mime type record #
The mediaType property of a MIME type record represents the MIME type of the NDEF record payload so that data can be properly decoded. For instance, use JSON.parse to decode JSON text and an Image element to decode image data.
functionreadMimeRecord(record){
console.assert(record.recordType ==="mime");
if(record.mediaType ==="application/json"){
const textDecoder =newTextDecoder();
console.log(`JSON: ${JSON.parse(decoder.decode(record.data))}`);
}
elseif(record.mediaType.startsWith('image/')){
const blob =newBlob([record.data],{ type: record.mediaType });
const img =newImage();
img.src =URL.createObjectURL(blob);
document.body.appendChild(img);
}
else{
// TODO: Handle other MIME types.
}
}
To write a MIME type record, pass an NDEF message dictionary to the NDEFReader write() method. The MIME type record contained in the NDEF message is defined as an object with a recordType key set to «mime», a mediaType key set to the actual MIME type of the content, and a data key set to an object that can be either an ArrayBuffer or provides a view on to an ArrayBuffer (e.g. Uint8Array, DataView).
const encoder =newTextEncoder();
const data ={
firstname:"François",
lastname:"Beaufort"
};
const jsonRecord ={
recordType:"mime",
mediaType:"application/json",
data: encoder.encode(JSON.stringify(data))
};const imageRecord ={
recordType:"mime",
mediaType:"image/png",
data:await(awaitfetch("icon1.png")).arrayBuffer()
};
const ndef =newNDEFReader();
await ndef.write({ records:[jsonRecord, imageRecord]});
Read and write a smart poster record #
A smart poster record (used in magazine advertisements, fliers, billboards, etc.), describes some web content as an NDEF record that contains an NDEF message as its payload. Call record.toRecords() to transform data to a list of records contained in the smart poster record.
It should have a URL record, a text record for the title, a MIME type record for the image, and some custom local type records such as «:t», «:act», and «:s» respectively for the type, action, and size of the smart poster record.
Local type records are unique only within the local context of the containing NDEF record. Use them when the meaning of the types doesn’t matter outside of the local context of the containing record and when storage usage is a hard constraint. Local type record names always start with :
functionreadSmartPosterRecord(smartPosterRecord){
console.assert(record.recordType ==="smart-poster");
let action, text, url;for(const record of smartPosterRecord.toRecords()){
if(record.recordType =="text"){
const decoder =newTextDecoder(record.encoding);
text = decoder.decode(record.data);
}elseif(record.recordType =="url"){
const decoder =newTextDecoder();
url = decoder.decode(record.data);
}elseif(record.recordType ==":act"){
action = record.data.getUint8(0);
}else{
// TODO: Handle other type of records such as `:t`, `:s`.
}
}
switch(action){
case0:
// Do the action
break;
case1:
// Save for later
break;
case2:
// Open for editing
break;
}
}
To write a smart poster record, pass an NDEF message to the NDEFReader write() method. The smart poster record contained in the NDEF message is defined as an object with a recordType key set to «smart-poster» and a data key set to an object that represents (once again) an NDEF message contained in the smart poster record.
Read and write a text record #
The text record data can be decoded with a TextDecoder instantiated with the record encoding property. Note that the language of the text record is available through its lang property.
functionreadTextRecord(record){
console.assert(record.recordType ==="text");
const textDecoder =newTextDecoder(record.encoding);
console.log(`Text: ${textDecoder.decode(record.data)} (${record.lang})`);
}
To write a simple text record, pass a string to the NDEFReader write() method.
const ndef =newNDEFReader();
await ndef.write("Hello World");
The makereadonly() method
The
NDEFReader.makeReadOnly
method, when invoked, MUST run the
make an NFC tag permanently read-only
algorithm:
- Let |p:Promise| be a new {{Promise}} object.
- If not currently executing in the currently active top-level
browsing context, then reject |p| with and
{{«InvalidStateError»}} {{DOMException}} and return |p|. - Let |options:NDEFMakeReadOnlyOptions| be the second argument.
- Let |signal:AbortSignal| be the |options|’ dictionary member
of the same name if present, or `null` otherwise. - If |signal| is [= AbortSignal/aborted =], then reject |p|
with |signal|’s [=AbortSignal/abort reason=] and return |p|. - If |signal| is not `null`, then
add the following abort steps to |signal|:- Run the abort a pending make read-only operation on the
environment settings object.
- Run the abort a pending make read-only operation on the
- [=promise/React=] to |p|:
- If |p| was settled (fulfilled or rejected), then clear the
pending write tuple if it exists.
- If |p| was settled (fulfilled or rejected), then clear the
- Return |p| and run the following steps in parallel:
- If the obtain permission steps return `false`, then
reject |p| with a {{«NotAllowedError»}} {{DOMException}} and
abort these steps. - If there is no underlying NFC Adapter, or if a connection
cannot be established, then reject |p| with a
{{«NotSupportedError»}} {{DOMException}}
and abort these steps. - If the UA is not allowed to access the underlying NFC Adapter
(e.g. a user preference), then reject |p| with a
{{«NotReadableError»}} {{DOMException}}
and abort these steps. - An implementation MAY reject |p| with
a {{«NotSupportedError»}} {{DOMException}}
and abort these steps.The UA might abort at this point. The reasons for termination
are implementation details. For example, the implementation
might be unable to support the requested operation. - Attempt to abort a pending make read-only operation.
A make read-only operation replaces all previously configured
make read-only operations. - Set pending makeReadOnly tuple to (`this`, |p|).
- Run the start the NFC make read-only steps whenever an
NFC tag |device| comes within communication range.If NFC is suspended, continue waiting until promise is
aborted by the user or an NFC tag comes within
communication range.
- If the obtain permission steps return `false`, then
The nfc standard
NFC is standardized in the NFC Forum and described in [[NFC-STANDARDS]].
Ndef compatible tag types
The NFC Forum has mandated the support of five different tag types to be
operable with NFC devices. The same is required on operating systems, such
as Android.
In addition to that, the MIFARE Standard specifies a way
for NDEF to work on top of the older MIFARE Standard, which may
be optionally supported by implementers.
A note about the NDEF mapping can be found here:
MIFARE Classic as NFC Type MIFARE Classic Tag.
- NFC Forum Type 1: This tag is based on the ISO/IEC 14443-3A
(NFC-A). The tags are rewritable and can be
configured to become read-only. Memory size can be between `96` bytes and
`2` Kbytes. Communication speed is `106` kbit/s. In contrast to all other
types, these tags have no anti-collision protection for dealing with multiple
tags within the NFC field. - NFC Forum Type 2: This tag is based on the
ISO/IEC 14443-3A (NFC-A). The tags are rewritable and can be configured
to become read-only. Memory size can be between `48` bytes and `2` Kbytes.
Communication speed is `106` kbit/s. - NFC Forum Type 3: This tag is based on the Japanese Industrial
Standard (JIS) X 6319-4 (ISO/IEC 18092), commonly known as FeliCa. The tags are
preconfigured to be either rewritable or read-only. Memory is `2` kbytes.
Communication speed is `212` kbit/s or `424` kbit/s. - NFC Forum Type 4: This tag is based on the ISO/IEC 14443-4 A/B
(NFC A, NFC B) and thus supports either NFC-A or NFC-B
for communication. On top of that the tag may optionally support ISO-DEP
(Data Exchange Protocol defined in ISO/IEC 14443 (ISO/IEC 14443-4:2008
Part 4: Transmission protocol). The tags are preconfigured
to be either rewritable or read-only. Variable memory, up to `32` kbytes.
Supports three different communication speeds `106` or `212` or
`424` kbit/s. - NFC Forum Type 5: This tag is based on ISO/IEC 15693 (NFC-V) and
allows reading and writing an NDEF message on an ISO/IEC 15693 RF tag
that is accessible by long range RFID readers as well. The NFC communication
is limited to short distance and may use the Active Communication Mode of
ISO/IEC 18092 where the sending peer generates the field which balances
power consumption and improves link stability. Variable memory, up to `64` kbytes.
Communication speed is `26.48` kbit/s. - MIFARE Standard: This tag, often sold under the brand names MIFARE
Classic or MIFARE Mini, is based on the ISO/IEC 14443-3A (also known as NFC-A,
as defined in ISO/IEC 14443-3:2021, Part 3: Initialization and anticollision).
The tags are rewritable and can be configured to become read-only. Memory size
can be between `320` and `4` kbytes. Communication speed is `106` kbit/s.MIFARE Standard is not an NFC Forum type and can only be read by devices
using NXP hardware. Support for reading and writing to tags based on the
MIFARE Standard is thus non-nominative, but the type is included
due to the popularity and use in legacy systems.
In addition to data types standardized for NDEF records by the NFC
Forum, many commercial products such as bus cards, door openers may be based
on the MIFARE Standard which requires specific NFC chips (same vendor of
card and reader) in order to function.
The write() method
The
NDEFReader.write
method, when invoked, MUST run the
write a message
algorithm:
- Let |p:Promise| be a new {{Promise}} object.
- If not currently executing in the currently active top-level
browsing context, then reject |p| with and
{{«InvalidStateError»}} {{DOMException}} and return |p|. - Let |message:NDEFMessageSource| be the first argument.
- Let |options:NDEFWriteOptions| be the second argument.
- Let |signal:AbortSignal| be the |options|’ dictionary member
of the same name if present, or `null` otherwise. - If |signal| is [= AbortSignal/aborted =], then reject |p|
with |signal|’s [=AbortSignal/abort reason=] and return |p|. - If |signal| is not `null`, then
add the following abort steps to |signal|:- Run the abort a pending write operation on the
environment settings object.
- Run the abort a pending write operation on the
- [=promise/React=] to |p|:
- If |p| was settled (fulfilled or rejected), then clear the
pending write tuple if it exists.
- If |p| was settled (fulfilled or rejected), then clear the
- Return |p| and run the following steps in parallel:
- If the obtain permission steps return `false`, then
reject |p| with a {{«NotAllowedError»}} {{DOMException}} and
abort these steps. - If there is no underlying NFC Adapter, or if a connection
cannot be established, then reject |p| with a
{{«NotSupportedError»}} {{DOMException}}
and abort these steps. - If the UA is not allowed to access the underlying NFC Adapter
(e.g. a user preference), then reject |p| with a
{{«NotReadableError»}} {{DOMException}}
and abort these steps. - If pushing data is not supported by the underlying
NFC Adapter, then reject |p| with a {{«NotSupportedError»}}
{{DOMException}} and abort these steps. - An implementation MAY reject |p| with
a {{«NotSupportedError»}} {{DOMException}}
and abort these steps.The UA might abort message write at this point. The reasons
for termination are implementation details. For example, the
implementation might be unable to support the requested
operation. - Let |output| be the notation for the NDEF message
to be created by UA, as the result of invoking
create NDEF message with |message|, `»»`, and `0`.
If this throws an exception, reject |p| with that
exception and abort these steps. - Attempt to abort a pending write operation.
A write replaces all previously configured write operations.
- Set `this`.[[WriteOptions]] to |options|.
- Set `this`.[[WriteMessage]] to |output|.
- Set pending write tuple to (`this`, |p|).
- Run the start the NFC write steps whenever an
NFC tag |device| comes within communication range.If NFC is suspended, continue waiting until promise is
aborted by the user or an NFC tag comes within
communication range.
- If the obtain permission steps return `false`, then
To
start the NFC write
, run these steps:
- Let |p:Promise| be the pending write tuple‘s promise.
- Let |writer| be the pending write tuple‘s writer.
- Let |options:NDEFWriteOptions| be |writer|.[[WriteOptions]].
- If the NFC tag in proximity range does not expose
NDEF technology for formatting or writing, then
reject |p| with a {{«NotSupportedError»}} {{DOMException}} and
return |p|. - Verify that NFC is not suspended.
- In case of success, run the following steps:
- If |device| is an NFC tag and if |options|’s overwrite is
`false`, read the tag to check whether there are NDEF
records on the tag. If yes, then reject |p| with a
{{«NotAllowedError»}} {{DOMException}} and return |p|. - Let |output:NDEFMessage| be |writer|.[[WriteMessage]].
- Initiate data transfer to |device| using
|output| as buffer, using the NFC adapter
in communication range with |device|.If the NFC tag in proximity range is unformatted and
NDEF-formatable, format it and write |output| as
buffer.Multiple adapters should be used sequentially by users.
There is very little likelihood that a simultaneous tap
will happen on two or multiple different and connected
NFC adapters.
If it happens, the user will likely need to repeat the
taps until success, preferably one device at a time.
The error here gives an indication that the operation
needs to be repeated. Otherwise the user may think the
operation succeeded on all connected NFC adapters. - If the transfer fails, reject |p| with
{{«NetworkError»}} {{DOMException}}
and abort these steps. - When the transfer has completed, resolve |p|.
- If |device| is an NFC tag and if |options|’s overwrite is
Чтение
Теперь рассмотрим методы чтения тега:
void NfcManager::readTarget(QNearFieldTarget *target)
{
connect(target, SIGNAL(error(QNearFieldTarget::Error,QNearFieldTarget::RequestId)), this, SLOT(errorHandler(QNearFieldTarget::Error,QNearFieldTarget::RequestId)));
connect(target, SIGNAL(ndefMessageRead(QNdefMessage)), this, SLOT(readRecords(QNdefMessage)));
target->readNdefMessages();
}
Чтение происходит в асинхронном режиме, поэтому в данном методе мы просто подключаем сигнал обработки ошибок и сигнал завершения чтения, который вызовется только в случае, если чтение произошло без ошибок.
После этого мы просто вызываем метод для чтения:
void NfcManager::readRecords(const QNdefMessage &message)
{
if (message.isEmpty())
return;
QNdefRecord record = message.at(0); // Read only first
readRecord(record);
}
Если чтение прошло успешно, то мы попадем в данный слот, где получим первую запись из списка присутсвующих на теге записей.
Да-да, по спецификации на теге может быть несколько записей, но как говорит документация, для
SymbianHarmattan
доступно чтение и запись только одной записи.
void NfcManager::readRecord(const QtMobility::QNdefRecord &record)
{
DataContainer *result = 0;
if (record.isRecordType<QNdefNfcUriRecord>()) {
QNdefNfcUriRecord uriRecord(record);
result = new UriDataContainer(uriRecord.payload(), uriRecord.uri().toString());
}
else if (record.isRecordType<QNdefNfcTextRecord>()) {
QNdefNfcTextRecord textRecord(record);
result = new TextDataContainer(textRecord.payload(), textRecord.text());
}
else if (record.isRecordType<NdefNfcSmartPosterRecord>()) {
NdefNfcSmartPosterRecord smartPosterRecord(record);
result = new SmartPosterDataContainer(smartPosterRecord.payload(), smartPosterRecord.uri().toString(), smartPosterRecord.title());
}
else {
result = new DataContainer(record.payload());
}
emit tagReadFinished(result);
}
И вот, после нескольких переходов по вспомогательным методам мы добрались до самого главного метода, который превращает информацию закодированную в теги в привычные нам буквы.
На данный момент
Qt Mobility
из коробки поддерживает только два вида записей это ссылки (
Uri
) и текст (
Text
), к третьему типу —
Smart Poster
мы еще вернемся ниже.
Как можно заметить данные из записи сразу помещаются в некий новый объект, это простые объекты, которые я специально создал для облегчения передачи данных в
QML
В конце вызывается сигнал, содержащий объект с данными. В дальнейшем мы будем ловить этот сигнал в QML.