Skip to content
Plugins (183)

nfc

stable

Encode and decode NFC Data Exchange Format (NDEF) messages and build ISO 7816-4 APDU commands for smart card communication.

use plugin nfc::{encode_text_record, encode_uri_record, encode_ndef_message, …}
14 functions Systems
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (14)
  1. encode_text_record Encode an NDEF Text Record (TNF=0x01)
  2. encode_uri_record Encode an NDEF URI Record with prefix abbreviation
  3. encode_ndef_message Wrap NDEF records in TLV-framed message bytes
  4. decode_ndef_message Parse raw NDEF bytes into a records table
  5. uri_prefix_code Get numeric code for a URI prefix string
  6. uri_prefix_from_code Get URI prefix string from numeric code
  7. apdu_select Build ISO 7816-4 SELECT APDU for an AID
  8. apdu_read Build ISO 7816-4 READ BINARY APDU
  9. apdu_write Build ISO 7816-4 UPDATE BINARY APDU
  10. parse_uid Format raw UID bytes as colon-separated hex
  11. tag_info Build a tag info summary from UID/ATQA/SAK
  12. detect_technology Detect NFC technology from ATQA and SAK
  13. decode_text_payload Decode an NDEF Text Record payload
  14. decode_uri_payload Decode an NDEF URI Record payload

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Overview

nfc is a dependency-free toolkit for the byte-level encoding work behind NFC tags and contactless smart cards. It has no live reader handle and holds no state — every function is a pure transform between Zolo values and raw bytes, so you pair it with whatever transport actually talks to hardware (a reader plugin, a Web NFC bridge, an APDU channel). Two layers live here: NDEF message building and parsing (text and URI records, TLV framing) and ISO 7816-4 APDU command construction (SELECT, READ BINARY, UPDATE BINARY) plus tag-identification helpers (UID formatting, ATQA/SAK technology detection).

The mental model is a pipeline of bytes. Build individual records with encode_text_record / encode_uri_record, wrap them with encode_ndef_message, and write them to a tag; on the way back, split a message with decode_ndef_message and expand each record's payload with decode_text_payload / decode_uri_payload. For smart-card I/O the apdu_* helpers emit ready-to-transmit command frames, while parse_uid, tag_info, and detect_technology turn the raw bytes a reader reports into readable summaries.

Common patterns

Build a complete NDEF message and turn it into a writable APDU command:

use plugin nfc::{encode_uri_record, encode_ndef_message, apdu_write}

let record = encode_uri_record("https://zolo-lang.dev")
let message = encode_ndef_message(#{1: record})
let cmd = apdu_write(0, message)
print("write command ready ({message} bytes wrapped)")

Decode a message read back from a tag, branching on each record's type:

use plugin nfc::{decode_ndef_message, decode_text_payload, decode_uri_payload}

let records = decode_ndef_message(raw_bytes)
for record in records {
  if record["type"] == "T" {
    let t = decode_text_payload(record["payload"])
    print("text [{t["language"]}]: {t["text"]}")
  } else if record["type"] == "U" {
    let u = decode_uri_payload(record["payload"])
    print("uri: {u["uri"]}")
  }
}

Identify a freshly detected tag from the bytes a reader reports:

use plugin nfc::{parse_uid, detect_technology, tag_info}

let info = tag_info(uid_bytes, atqa_bytes, 0x08)
print("UID {info["uid"]} is a {info["tag_type"]}")
print("speaks {detect_technology(atqa_bytes, 0x08)}")

Encode an NDEF Text Record (TNF=0x01)

Encodes a UTF-8 text string into an NDEF Text Record byte sequence (TNF=0x01, type="T"). The status byte encodes the language code length; the record is a complete short record with MB and ME flags set.

use plugin nfc::{encode_text_record}

let record = encode_text_record("Hello NFC", "en")
print("record bytes: {record}")

The language code drives the status byte, so a non-English tag just changes the second argument before wrapping into a message:

use plugin nfc::{encode_text_record, encode_ndef_message}

let pt = encode_text_record("Olá mundo", "pt")
let message = encode_ndef_message(#{1: pt})
print("Portuguese text record wrapped")

Encode an NDEF URI Record with prefix abbreviation

Encodes a URI into an NDEF URI Record, automatically selecting the longest matching prefix abbreviation from the NFC Forum table (e.g., https:// becomes code 0x04). Use this before passing to encode_ndef_message.

use plugin nfc::{encode_uri_record, encode_ndef_message}

let rec = encode_uri_record("https://zolo-lang.dev")
let msg = encode_ndef_message(#{1: rec})
print("NDEF message ready to write")

Wrap NDEF records in TLV-framed message bytes

Wraps one or more raw NDEF record byte arrays in a TLV-framed NDEF Message (type 0x03, terminated by 0xFE). Adjusts the MB/ME flags on each record automatically.

use plugin nfc::{encode_text_record, encode_ndef_message}

let r1 = encode_text_record("Title", "en")
let msg = encode_ndef_message(#{1: r1})

Passing more than one record produces a multi-record message; the MB/ME flags are corrected so the first record opens and the last record closes the message:

use plugin nfc::{encode_text_record, encode_uri_record, encode_ndef_message}

let title = encode_text_record("Zolo", "en")
let link = encode_uri_record("https://zolo-lang.dev")
let msg = encode_ndef_message(#{1: title, 2: link})
print("two-record NDEF message built")

Parse raw NDEF bytes into a records table

Parses a raw NDEF message byte sequence (without TLV wrapper) into a table of records. Each record entry has keys tnf, type, and payload.

use plugin nfc::{decode_ndef_message}

let records = decode_ndef_message(raw_bytes)
let first = records[1]
print("record type: {first["type"]}")

Iterate every record to inspect the type-name fields the parser extracts:

use plugin nfc::{decode_ndef_message}

let records = decode_ndef_message(raw_bytes)
for record in records {
  print("tnf={record["tnf"]} type={record["type"]}")
}

Get numeric code for a URI prefix string

Returns the numeric NFC Forum URI prefix code for a given prefix string (e.g., "https://" → 4). Returns -1 if the prefix is not in the table.

use plugin nfc::{uri_prefix_code}

let code = uri_prefix_code("https://")
print("code: {code}")

Get URI prefix string from numeric code

Returns the URI prefix string for a given NFC Forum numeric code. Returns an empty string for unknown codes.

use plugin nfc::{uri_prefix_from_code}

let prefix = uri_prefix_from_code(4)
print("prefix: {prefix}")

Build ISO 7816-4 SELECT APDU for an AID

Builds an ISO 7816-4 SELECT command APDU for the given AID (Application Identifier) specified as a hex string. Use when initiating communication with a smart card applet.

use plugin nfc::{apdu_select}

let cmd = apdu_select("D2760000850101")
print("SELECT APDU ready")

Build ISO 7816-4 READ BINARY APDU

Builds an ISO 7816-4 READ BINARY APDU to read length bytes starting at offset from the currently selected file or applet.

use plugin nfc::{apdu_read}

let cmd = apdu_read(0, 32)

Walk a file in fixed-size pages by advancing the offset between reads:

use plugin nfc::{apdu_read}

let page = 0
while page < 4 {
  let cmd = apdu_read(page * 16, 16)
  print("read page {page}")
  page = page + 1
}

Build ISO 7816-4 UPDATE BINARY APDU

Builds an ISO 7816-4 UPDATE BINARY APDU to write data bytes at the given offset. The data must be at most 255 bytes.

use plugin nfc::{apdu_write, encode_ndef_message, encode_text_record}

let msg = encode_ndef_message(#{1: encode_text_record("hello", "en")})
let cmd = apdu_write(0, msg)

Format raw UID bytes as colon-separated hex

Converts raw NFC tag UID bytes into a human-readable colon-separated uppercase hex string (e.g., "04:A3:2B:1C").

use plugin nfc::{parse_uid}

let uid_str = parse_uid(uid_bytes)
print("UID: {uid_str}")

Build a tag info summary from UID/ATQA/SAK

Builds a summary table for an NFC-A tag from its UID, ATQA, and SAK bytes. Returns {uid, atqa, sak, tag_type, uid_length} where tag_type identifies common chip families (MIFARE Ultralight, Classic, DESFire, etc.).

use plugin nfc::{tag_info}

let info = tag_info(uid_bytes, atqa_bytes, 0x08)
print("tag: {info["tag_type"]}")
print("uid: {info["uid"]}")

Detect NFC technology from ATQA and SAK

Determines the NFC communication technology (e.g., "NFC-A (ISO 14443-4)") from the ATQA presence and SAK bit flags. Useful for routing to the correct protocol handler.

use plugin nfc::{detect_technology}

let tech = detect_technology(atqa_bytes, 0x20)
print("technology: {tech}")

Decode an NDEF Text Record payload

Decodes an NDEF Text Record payload bytes into {language, text, encoding}. Useful after extracting the payload field from decode_ndef_message.

use plugin nfc::{decode_ndef_message, decode_text_payload}

let records = decode_ndef_message(raw_bytes)
let decoded = decode_text_payload(records[1]["payload"])
print("{decoded["language"]}: {decoded["text"]}")

Decode an NDEF URI Record payload

Decodes an NDEF URI Record payload bytes into {prefix, uri}, expanding the prefix abbreviation code into the full prefix string and reconstructing the complete URI.

use plugin nfc::{decode_uri_payload}

let result = decode_uri_payload(payload_bytes)
print("URI: {result["uri"]}")

Round-trips with encode_uri_record: the abbreviation code is expanded back into the original prefix, so the reconstructed URI matches what you encoded:

use plugin nfc::{encode_uri_record, decode_ndef_message, decode_uri_payload}

let record = encode_uri_record("https://www.example.com")
let parsed = decode_ndef_message(record)
let result = decode_uri_payload(parsed[1]["payload"])
print("prefix: {result["prefix"]}")
print("uri: {result["uri"]}")

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en