Skip to content
Language: EN

How do you send a raw command to a tag?

Through the class or protocol that matches the tag's radio layer: NfcA, NfcV or IsoDep on Android, NFCMiFareTag, NFCISO15693Tag or NFCISO7816Tag in an iOS tag session. The code builds the command bytes, the platform adds the framing, and the answer comes back as bytes that only the code can judge. Web NFC sends none.

Pick the channel from the radio layer

On Android, the technology classes that send bytes as they are all expose transceive(). NfcA reaches NFC-A tags, every Type 2 tag among them; NfcV reaches ISO/IEC 15693 tags; IsoDep carries APDUs to ISO/IEC 14443-4 tags and cards. MifareUltralight.transceive() is, according to its reference, the same as connecting through NfcA and calling its method.

On iOS, raw commands need a tag session, since an NDEF session hands over no command channel, as the session page explains. The detected tag arrives as one of the protocols Core NFC defines: NFCMiFareTag, whose sendMiFareCommand serves MIFARE Ultralight, Plus and DESFire tags but not the Crypto1 protocol of MIFARE Classic; NFCISO15693Tag, with a method for each standard command; NFCISO7816Tag, whose sendCommand(apdu:) sends APDUs; and NFCFeliCaTag for FeliCa.

A web page has no channel at all. Web NFC stops at NDEF by design, as the Web NFC writing page describes, so a project that needs raw commands needs an app.

What your bytes contain

The platform frames what the code sends, and each reference says where that framing stops. NfcA.transceive() takes complete bytes and adds the CRC itself: the NfcA reference tells applications not to append it, and rules out the commands used for polling. NfcV.transceive() expects the flags byte, the command code and its parameters, and adds the CRC too. IsoDep.transceive() takes the APDU alone, without frame indicators, and splits a long one itself.

Core NFC does not draw the line in the same place. Apple's sendMiFareCommand reference asks the app to calculate the two CRC bytes of a MIFARE Ultralight family command and append them, the opposite of the Android rule, so a command copied from Android code arrives without them. The ISO 15693 methods build the whole request, and by default address it to the tag's identifier.

Whatever the platform, a command that no tag type defines belongs to one chip. Its code and its parameters come from that chip's datasheet, which this site leaves to the chip fiches on wakdev.com.

Reading the answer

A call that returns has delivered bytes, and that is all the platform checked. Whether the tag accepted the command is written inside those bytes, in a format that belongs to the protocol.

An ISO 15693 tag opens its answer with a flags byte whose lowest bit reports an error, followed by an error code, according to the definitions of Android's open source NFC stack. Core NFC turns such an answer into an error whose user information carries NFCISO15693TagResponseErrorKey, with the code from the standard.

An APDU ends with two status bytes, 90 00 when the command completed. Apple's sendCommand reference returns them as sw1 and sw2, always valid, and keeps its error for communication problems: a card that refuses a command answers normally, with the refusal in the status bytes. The APDU page lists the common values.

Two kinds of failure

A transport failure means no usable answer arrived. On Android, the call throws TagLostException when the tag has left the field and IOException for other failures. On iOS, the error codes include readerTransceiveErrorTagConnectionLost, readerTransceiveErrorPacketTooLong for a command longer than the tag accepts, and readerTransceiveErrorTagResponseError when the tag answered with an error.

A write that ends in a transport failure has an unknown outcome. The command may have reached the tag before its answer was lost, so read the memory back before sending anything again, and write in an order that never leaves a half valid message, the subject of the interrupted writes page.

Limits apply before any of this. How many bytes one call can carry, and how long it waits, differ from one Android phone to the next, as the controller limits page explains: a command that works on the test phone can fail on another before it reaches the tag.

Frequently asked questions

Can a web page send a raw command to a tag?

No. Web NFC reads and writes NDEF only, so raw commands need an Android or iOS app.

Does my code add the CRC?

Not on Android, where NfcA and NfcV add it. Apple's reference asks for it on MIFARE Ultralight family commands sent with sendMiFareCommand.

Why does a command return without doing anything?

The platform reports a delivered answer, not an accepted command. Read the error flag of an ISO 15693 answer, or the status bytes of an APDU, before trusting the result.

Where do the commands of a particular chip come from?

From that chip's datasheet. This section covers the commands the NFC Forum tag types define, and the chip fiches on wakdev.com cover the rest.