Indigresso Wiki

Open Source Stuff for DASH7

User Tools

Site Tools


NDEF (OTlib)

OpenTag implements a reduced-scope version of NDEF for use in data wrapping. The NDEF library is part of OTlib, and as such it is consistent across platforms. The OpenTag NDEF library is primarily used over the Message Pipe (MPipe), but it is also used in truncated form for wrapping DASH7 Mode 2 ALPs (see also OpenTag ALP module). In OpenTag, ALP module is intertwined with NDEF to the extent that NDEF usage in OpenTag is primarily for the purpose of enabling NDEF-aware clients to communicate with OpenTag more easily over MPipe.

Normal Form

In normal form, OpenTag's implementation of NDEF matches that of the NFC specification, although many of the features available in the NFC specification are removed in order to keep the implementation light.

Feature Differences Between NFC NDEF and OpenTag NDEF
Notes OpenTag NDEF OpenTag ALP NFC NDEF
Multi-record Message MB and ME flags can be set independently Available Available Available
Fragmented Records CF (Chunk Flag) can be set Experimental Experimental Available
NDEF Type Support Support for types other than “Unknown” Not Planned N/A Available
Long Record Support Support for 4-byte Payload Length field N/A N/A Available
Short Record Support Support for 1-byte Payload Length field Mandatory Mandatory Available
Fixed ID (2-byte) ID field is always two-bytes Available Mandatory Available
Dynamic ID (N byte) ID field can be 0-255 bytes Available N/A Available

Normal Form NDEF Header

The “Normal Form” OpenTag NDEF Header is generally 6 bytes in length. While non-two-byte ID's are supported, they will be ignored by other layers of OpenTag uses NDEF exclusively for ALPs and ALP APIs, and these require a two-byte ID (first byte is ALP ID, second byte is ALP command). It may be useful to look at the DASH7 Mode 2 ALP Framework to get an understanding of how each ID byte can be used, although it may also be sufficient just to know that there are 256 possible ALP ID's (most are unused and undefined), and each ALP can have 128 independent commands (the MSBit in the command byte is used to enable a response).

Normal Form NDEF Record
Record Flags Type Length Payload Length ID Length ID Payload
1 Byte 1 Byte 1 Byte 1 Byte 2 Bytes N Bytes
(bitfield) (always 0) N (integer) (always 2) (See ALP) Data
Normal Form Record Flags
b7 b6 b5 b4 b3 b2 b1 b0
  • MB: Message Begin, set when record is first in the message
  • ME: Message End, set when record is the last in the message
  • CF: Chunk Flag, set when the record is a fragment, and there is a continuation in the next record
  • SR: Short Record, set when record uses 1-byte length field (instead of 4-byte)
  • IL: ID Length (always set), set when ID length field exists in the header (and hence ID exists)
  • TNF: Type Name Field (always 5 or 6), a 3-bit number describing a payload format
    • Use 6 (Unknown) on all non-fragmented records, or on the first fragment of a fragmented record
    • Use 5 (Unchanged) when the record is a subsequent fragment/chunk of a previous record

ID Fields

The ID Fields are present in Normal Form and all other forms. OpenTag (and DASH7 Mode 2) support up to 256 ALPs, of which the DASH7 standard currently reserves a few and OpenTag current defines a few more. Most of the ALP space is unused, and some of it is reserved for proprietary usage, so there's no shortage of ALP IDs. Each ALP can appropriate 128 commands. The MSBit of the ALP Cmd field is reserved, and it is used to inform the recipient of the command that it should form a response of some sort. For more information, check the ALP section.

ID Fields
ID Length ALP ID ALP Cmd
1 Byte 1 Byte 1 Byte
(always 2) 0-255 0-255

Truncated Form (ALP Form)

Main Article for ALP
ALPs use a 4 byte header instead of the Normal Form 6 byte header. The Type Length and ID Length fields are removed, because they are superfluous (Type Length is always 0, ID Length is always 2). Additionally, bits b4:0 in the record flags are formally “don't care” bits, although it is best-practice to use the same values in these flag bitfields as you would in Normal Form. This is especially true for the SR and IL bits, which in unofficial builds may optimize-out some “safety check” code – the TNF field may safely be ignored in any case, because it is redundant to the CF field in the OpenTag NDEF library.

MPipe Form

Main Article for MPipe
MPipe uses the Normal Form packet, and it adds a 4-byte footer. The footer is not factored-in to the NDEF payload size, but it is always the last four bytes of the message, following the payload.


The code from ndef.h is pasted below, although you can also check the doxygen code documentation.

#include "OT_config.h"


#include "OT_types.h"
#include "OTAPI_tmpl.h"
#include "alp.h"
#include "queue.h"

/** NDEF Record Control Bits 
  * In many (if not all) cases, these control bits are applied automatically by
  * the Record/Message constructor functions.
#define NDEF_MB                 0x80    // Message Start bit
#define NDEF_ME                 0x40    // Message End bit
#define NDEF_CF                 0x20    // Chunk Flag
#define NDEF_SR                 0x10    // Short Record indicator
#define NDEF_IL                 0x08    // ID LENGTH field indicator

/** NDEF TNF Field Values
  * Constants and an enum to define the TNF (Type Name Field).  TNF is a 3 bit
  * id that goes in the same Message/Record byte as the NDEF Control Bits.
  * OpenTag currently uses the "UNKNOWN" TNF exclusively, which means that the
  * Type field is omitted, and the client/server are implicitly programmed to
  * only deal with TNFs of this type.
#define NDEF_TNF_EMPTY          0x00
#define NDEF_TNF_WELLKNOWN      0x01    // You could use this type in local context
#define NDEF_TNF_MEDIATYPE      0x02
#define NDEF_TNF_ABSOLUTEURI    0x03
#define NDEF_TNF_EXTERNAL       0x04    // or this type in any context
#define NDEF_TNF_UNKNOWN        0x05    // or this type when implicit knowledge is OK
#define NDEF_TNF_UNCHANGED      0x06
#define NDEF_TNF_RESERVED       0x07

typedef enum {
    TNF_Empty = 0x00,
} NDEF_tnf;

typedef enum {
    MSG_Complete    = 0,
    MSG_Incomplete  = 1,
    MSG_Chunking_In = 2,
    MSG_Chunking_Out= 3
} NDEF_status;

/** ndef_message is an internal data store that is exposed only for purposes of
  * transparency (this is an open source project).
typedef struct {
    //Queue*  msgq;
    ot_u8   last_flags;
    //ot_u8   msg_tnf;
    //ot_int  msg_records;
} ndef_message;

//If you want to expose the message data store, uncomment this.
//extern ndef_message ndef;

/** @brief  Begins a new message.  A message can contain one or more records.
  * @param  output      (Queue*) Pointer to Queue where record/message is buffered
  * @retval ot_bool      False/True when output parameter is Null/Non-Null
  * @ingroup NDEF
  * The output parameter should point to a Queue that is persistent in memory or
  * at lest persistent during the processing of the message.  In other terms, if
  * you have the Queue declared in the C stack (i.e. within a function), make 
  * sure that function does not return prior to the message being sent (this 
  * will cause the C stack to pop the Queue declaration before the NDEF message
  * is finished operating on it).  If you declare the Queue in persistent 
  * memory, there is nothing to worry about.
  * If otapi_new_ndefmsg is called while another message is being built, the
  * latter call will wipe-out the prior message.
ot_bool ndef_new_msg(Queue* output);

/** @brief  Creates a new record.  A message can contain one or more records.
  * @param  record      (alp_record*) Pointer to struct containing record attributes
  * @param  record_data (ot_u8*) Payload data for the record
  * @param  output      (Queue*) Queue for output message (usually &dir_out)
  * @retval ot_bool     False/True on error/no error
  * @ingroup NDEF
  * The record payload does not need to be double buffered.  If parameter 
  * record_data == NULL, no bytes will be written to the output queue,
  * and the queue putcursor will stay at the insertion point of the payload.
  * This allows the user to manually load data into the record via the queue
  * write functions (see queue.h).  If the user fails to load in the appropriate
  * amount of bytes, as indicated by the record->payload_length parameter, the
  * next call to otapi_new_ndefrec() or otapi_send_ndefmsg() will automatically 
  * crop or pad the payload in order to make sure that the length value written
  * to the record matches exactly the number of bytes in the payload.  While 
  * this feature will protect the record structure, it may corrupt the data. 
  * Therefore, the user should take steps to ensure the payload data is loaded
  * to the output queue accurately.
  * The return value will report an error when the record parameter is 
  * malformed.  On error, nothing will be written the message queue.
ot_bool ndef_new_record(alp_record* record, ot_u8* record_data, Queue* output);

/** @brief  Sends over Mpipe the NDEF message stored in the suppiled queuep
  * @param  output      (Queue*) Queue containing output data from Queue.front
  * @retval ot_int      error if negative, if non-negative it equals the amount of bytes in mpipe
  * @ingroup NDEF
  * ndef_send_msg() will automatically align the data in the queue to match the
  * specifications of the NDEF message & record headers.  Then it will push this
  * data to the Mpipe.
ot_int ndef_send_msg(Queue* output);

/** @brief  Loads a queue into the NDEF module, for later parsing
  * @param  input       (Queue*) Pointer to Queue where record/message is buffered
  * @retval ot_bool     False/True when input parameter is Null/Non-Null
  * @ingroup NDEF
  * @sa ndef_send_msg()
  * This is the RX version of ndef_send_msg().  It does not actually parse any
  * data, but it must be called before calling otapi_parse_ndefrec() is called.
ot_bool ndef_load_msg(Queue* input);

/** @brief  Parses a received NDEF record
  * @param  in_rec      (alp_record*) Data for input record
  * @param  out_rec     (alp_record*) Data for output record
  * @param  in_q        (Queue*) Input data queue
  * @param  out_q       (Queue*) Output data queue
  * @retval NDEF_status Enumerated value of parsing & message status
  * @ingroup NDEF
  * Call this one or more times following a call to ndef_load_msg().  Stop
  * calling it once it returns MSG_Complete or when the input Queue is out of 
  * data -- you will need to use another function, or group of functions, to
  * manage the I/O (check for some helper functions in the platform layer code
  * especially in Mpipe code).  
  * In almost all circumstances, out_q should be &dir_out (see buffers.h). in_q
  * is more flexible, but it is usually &dir_in.  The parameters for in_rec and
  * out_rec just need to be allocated by the caller.  The caller can use them
  * for additional visibility into the NDEF parsing process, if desired.
  * @note Parsing Capability Limitations
  * This version of the function is pre-release and it is not designed to work
  * with record chunking (on input or output) or NDEF messages longer than the
  * input or output queues (nominally, 256 bytes).
  * @note Usability of NDEF within OpenTag
  * At this time, OpenTag uses only a subset of NDEF.  Therefore, any records
  * that do not conform to this feature subset will be ignored.  The subset is
  * of the "UNKNOWN" TNF, where the ID is two bytes and the payload contents are
  * defined by the ID bytes.  The documentation at the top of this header file
  * goes into greater detail on this topic.
NDEF_status ndef_parse_record(alp_record* in_rec, alp_record* out_rec, Queue* in_q, Queue* out_q);

/** Bonus functions (implementation is optional, and pending).  These are 
  * similar in design to Android (Gingerbread) NDEF API, although they are not
  * designed in a way that makes much sense for embedded usage (Not my fault,
  * Google's fault for designing an API with lots of data bufferring, a typical
  * design problem of OO projects).
  * If you have a heavy client (like a PC or an OMAP), you may seek to implement
  * these on the client side.  They will probably never go into the server side.

///void ndef_assemble_record(Queue* output, ndef_record* record);
///void ndef_assemble_message(Queue* output, ot_int num_records, ndef_record* record_array);
///void ndef_copy_record(Queue* output, ot_uint data_length ot_u8* data);
///void ndef_copy_message(Queue* output, ot_uint data_length ot_u8* data);

opentag/otlib/ndef.txt · Last modified: 2012/03/24 20:47 by jpnorair